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; } |