天天看點

QT中的多線程(一)

QT中的多線程(一) 2010-05-20 17:35

這篇文章和以下的一篇文章 QT中的多線程(二)皆為轉載文章,文章轉自:

http://www.cppblog.com/yuanyajie/archive/2007/08/22/30599.html

十分感謝原文作者,幫我解決了近幾日的疑惑,對于QT的線程間的通信有了進一步了解,在這裡向他緻以我崇高的敬意。

QT通過三種形式提供了對線程的支援。它們分别是,一、 平台無關 的線程類,二、 線程安全的事件投遞,三、 跨線程的信号-槽連接配接。這使得開發輕巧的多線程Qt程式更為容易, 并能充分利用多處理器機器的優勢。多線程程式設計也是一個有用的模式,它用于解決執行較長時間的操作而不至于使用者界面失去響應。在Qt的早期版本中,在建構庫 時有不選擇線程支援的選項,從4.0開始,線程總是有效的。

線 程類

Qt 包含下面一些線程相關的類:

QThread 提供了開始一個新線程的方法

QThreadStorage 提供逐線程資料存儲

QMutex  提供互相排斥的鎖,或互斥量

QMutexLocker 是一個便利類,它可以自動對 QMutex加鎖與解鎖

QReadWriterLock 提供了一個可以同時讀操作的鎖

QReadLocker與 QWriteLocker 是便利類,它自動對 QReadWriteLock加鎖與解鎖

QSemaphore 提供了一個整型信号量,是互斥量的泛化

QWaitCondition 提供了一種方法,使得線程可以在被另外線程喚醒之前一直休眠。

建立一個線程

為建立一個線程,子類化QThread并且重寫它的 run()函數,例如:

class MyThread : public QThread

{

Q_OBJECT

 protected:

void run();

};

 void MyThread::run()

{

...

}

之後,建立這個線程對象的執行個體,調用 QThread::start()。于是,在run()裡出現的代碼将會在另外線程中被執行。

注 意:QCoreApplication::exec()必 須總是在主線程(執行main()的那個線程)中被調用,不能從一個QThread中調用。在GUI程式中,主線程也被稱為GUI線程,因為它是唯一一個 允許執行GUI相關操作的線程。另外,你必須在建立一個QThread之前建立QApplication(or QCoreApplication)對象。

線程同步

QMutex, QReadWriteLock, QSemaphore,  QWaitCondition 提供了線程同步的手段。使用線程的主要想法是希望它們可以盡可能并發執行,而一些關鍵點上線程之間需要停止或等待。例如,假如兩個線程試圖同時通路同一個 全局變量,結果可能不如所願。

QMutex  提供互相排斥的鎖,或互斥量。在一個時刻至多一個線程擁有mutex,假如一個線程試圖通路已經被鎖定的mutex,那麼它将休眠,直到擁有mutex的 線程對此mutex解鎖。Mutexes常用來保護共享資料通路。

QReadWriterLock 與QMutex相似,除了它對 "read","write"通路進行差別對待。它使得多個讀者可以共時通路資料。使用QReadWriteLock而不是QMutex,可以使得多線程 程式更具有并發性。

QReadWriteLock lock;

void ReaderThread::run()

{

// ...

lock.lockForRead();

read_file();

lock.unlock();

//...

 }

 void WriterThread::run()

{

  // ...

     lock.lockForWrite();

write_file();

lock.unlock();

   // ...

 }

QSemaphore 是QMutex的一般化,它可以保護一定數量的相同資源,與此相對,一個 mutex隻保護一個資源。下面例子中,使用QSemaphore來 控制對環狀緩沖的通路,此緩沖區被生産者線程和消費者線程共享。生産者不斷向緩沖寫入資料直到緩沖末端,再從頭開始。消費者從緩沖不斷讀取資料。信号量比 互斥量有更好的并發性,假如我們用互斥量來控制對緩沖的通路,那麼生産者,消費者不能同時通路緩沖。然而,我們知道在同一時刻,不同線程通路緩沖的不同部 分并沒有什麼危害。

 const int DataSize = 100000;

const int BufferSize = 8192;

char buffer[BufferSize];

 QSemaphore freeBytes(BufferSize);

QSemaphore usedBytes;

 class Producer : public QThread

{

public:

void run();

};

 void Producer::run()

{

qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

for (int i = 0; i < DataSize; ++i) {

freeBytes.acquire();

buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];

usedBytes.release();

}

}

 class Consumer : public QThread

{

public:

void run();

};

 void Consumer::run()

{

for (int i = 0; i < DataSize; ++i) {

usedBytes.acquire();

fprintf(stderr, "%c", buffer[i % BufferSize]);

freeBytes.release();

}

fprintf(stderr, "/n");

}

 int main(int argc, char *argv[])

{

QCoreApplication app(argc, argv);

Producer producer;

Consumer consumer;

producer.start();

consumer.start();

producer.wait();

consumer.wait();

return 0;

}

QWaitCondition 允許線程在某些情況發生時喚醒另外的線程。一個或多個線程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()設定一個條件。wakeOne()随機喚醒一個,wakeAll()喚醒所有。

下面的例子中,生産者首先必須檢查緩沖是否已滿(numUsedBytes==BufferSize),如果是,線程停下來等待 bufferNotFull條件。如果不是,在緩沖中生産資料,增加numUsedBytes,激活條件 bufferNotEmpty。使用mutex來保護對numUsedBytes的通路。另外,QWaitCondition::wait()接收一個mutex作為參數,這個mutex應該被調用 線程初始化為鎖定狀态。線上程進入休眠狀态之前,mutex會被解鎖。而當線程被喚醒時,mutex會處于鎖定狀态,而且,從鎖定狀态到等待狀态的轉換是 原子操作,這阻止了競争條件的産生。當程式開始運作時,隻有生産者可以工作。消費者被阻塞等待bufferNotEmpty條件,一旦生産者在緩沖中放入 一個位元組,bufferNotEmpty條件被激發,消費者線程于是被喚醒。

 const int DataSize = 100000;

const int BufferSize = 8192;

char buffer[BufferSize];

 QWaitCondition bufferNotEmpty;

QWaitCondition bufferNotFull;

QMutex mutex;

int numUsedBytes = 0;

 class Producer : public QThread

{

public:

void run();

};

 void Producer::run()

{

qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

     for (int i = 0; i < DataSize; ++i) {

mutex.lock();

if (numUsedBytes == BufferSize)

bufferNotFull.wait(&mutex);

mutex.unlock();

         buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];

         mutex.lock();

++numUsedBytes;

bufferNotEmpty.wakeAll();

mutex.unlock();

}

}

 class Consumer : public QThread

{

public:

void run();

};

 void Consumer::run()

{

for (int i = 0; i < DataSize; ++i) {

mutex.lock();

if (numUsedBytes == 0)

bufferNotEmpty.wait(&mutex);

mutex.unlock();

         fprintf(stderr, "%c", buffer[i % BufferSize]);

         mutex.lock();

--numUsedBytes;

bufferNotFull.wakeAll();

mutex.unlock();

}

fprintf(stderr, "/n");

}

 int main(int argc, char *argv[])

{

QCoreApplication app(argc, argv);

Producer producer;

Consumer consumer;

producer.start();

consumer.start();

producer.wait();

consumer.wait();

return 0;

}