日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Qt之线程同步(生产者消费者模式 - QWaitCondition)

發(fā)布時間:2025/3/15 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt之线程同步(生产者消费者模式 - QWaitCondition) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


簡述

生產者將數(shù)據(jù)寫入緩沖區(qū),直到它到達緩沖區(qū)的末尾,這時,它從開始位置重新啟動,覆蓋現(xiàn)有數(shù)據(jù)。消費者線程讀取數(shù)據(jù)并將其寫入標準錯誤。

Wait condition(等待條件)比單獨使用 mutex(互斥量)有一個更高級的并發(fā)性,如果緩沖區(qū)的訪問由一個 QMutex 把守,當生產者線程訪問緩沖區(qū)時,消費者線程將無法訪問。然而,兩個線程同時訪問不同的緩沖區(qū)是沒有害處的。

示例包含兩個類:Producer 和 Consumer,均繼承自 QThread。循環(huán)緩沖區(qū)用于兩個類之間的溝通,同步工具用于保護全局變量。

另一種“生產者 - 消費者”模式的方案是使用 QSemaphore - Qt之線程同步(生產者消費者模式 - QSemaphore)

  • 簡述
  • 全局變量
  • Producer
  • Consumer
  • main 函數(shù)
  • 更多參考

全局變量

首先,一起來看循環(huán)緩沖區(qū)和相關的同步工具:

const int DataSize = 100000;const int BufferSize = 8192; char buffer[BufferSize];QWaitCondition bufferNotEmpty; QWaitCondition bufferNotFull; QMutex mutex; int numUsedBytes = 0;

DataSize 是生產者將生成的數(shù)據(jù)數(shù)量,為了讓示例盡可能地簡單,把它定義為一個常數(shù)。BufferSize 是循環(huán)緩沖區(qū)的大小,小于 DataSize,這意味著在某一時刻生產者將達到緩沖區(qū)的末尾,并從開始位置重新啟動。

要同步生產者和消費者,需要兩個 wait 條件和一個 mutex。當生產者生成一些數(shù)據(jù)時,bufferNotEmpty 條件被發(fā)射,告訴消費者可以讀取它了;當消費者讀取一些數(shù)據(jù)時,bufferNotFull 條件被發(fā)射,告訴生產者生成更多的數(shù)據(jù)。numUsedBytes 為緩沖區(qū)中所包含數(shù)據(jù)的字節(jié)數(shù)。

總之,wait 條件、mutex、和 numUsedBytes 計數(shù)器確保生產者不會先于消費者超過 BufferSize 的大小,而消費者永遠不會讀取生產者尚未生成的數(shù)據(jù)。

Producer

Producer 類的代碼如下:

class Producer : public QThread { public:Producer(QObject *parent = NULL) : QThread(parent){}void run() Q_DECL_OVERRIDE{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();}} };

生產者生成 DataSize 字節(jié)的數(shù)據(jù)。在往循環(huán)緩沖區(qū)寫入一個字節(jié)之前,它必須先檢測緩沖區(qū)是否已滿(例如,numUsedBytes 等于 BufferSize),如果緩沖區(qū)滿了,線程就會在 bufferNotFull 條件上等待。

最后,生產者增加 numUsedBytes,并且標志 bufferNotEmpty 條件為 true,從而 numUsedBytes 必然大于 0,

我們使用一個 mutex 保護 numUsedBytes 變量的所有訪問。此外,QWaitCondition::wait() 函數(shù)接受一個 mutex 作為其參數(shù)。當線程被置于休眠狀態(tài)之前,該 mutex 被解鎖;當線程被喚醒,該 mutex 被鎖定。此外,為了防止競爭條件發(fā)生,從鎖定狀態(tài)到等待狀態(tài)的過渡具有原子性。

Consumer

現(xiàn)在轉向 Consumer 類:

class Consumer : public QThread {Q_OBJECT public:Consumer(QObject *parent = NULL) : QThread(parent){}void run() Q_DECL_OVERRIDE{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");}signals:void stringConsumed(const QString &text); };

代碼非常類似于生產者,在讀取字節(jié)之前,需要先檢查緩沖區(qū)是否為空(numUsedBytes 為 0),而非它是否為已滿。并且,當它為空時,等待 bufferNotEmpty 條件。在讀取字節(jié)后,減小 numUsedBytes (而非增加),并標志 bufferNotFull 條件(而非 bufferNotEmpty 條件)。

main() 函數(shù)

在 main() 函數(shù)中,我們創(chuàng)建兩個線程,并調用 QThread::wait(),以確保在退出之前,這兩個線程有時間完成。

int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);Producer producer;Consumer consumer;producer.start();consumer.start();producer.wait();consumer.wait();return 0; }

當運行這個程序時,會發(fā)生什么呢?

最初,生產者是唯一一個可以做任何事情的線程,消費者阻塞并等待 bufferNotEmpty 條件被發(fā)射(numUsedBytes 是 0)。一旦生產者把一個字節(jié)放入緩沖區(qū),numUsedBytes 就會變?yōu)?BufferSize - 1,并且 bufferNotEmpty 條件被發(fā)射。這時,可能發(fā)生兩件事:要么消費者線程接管和讀取字節(jié),要么生產者開始生成第二個字節(jié)。

此示例中提出的“生產者 - 消費者”模式,適用于編寫高并發(fā)多線程應用。在多處理器計算機中,程序可能比基于 mutex 的方案快達兩倍之多,因為兩個線程可以同一時間在緩沖區(qū)的不同部分處于激活狀態(tài)。

要知道,這些好處并不總能實現(xiàn),加鎖和解鎖一個 QMutex 是需要成本的。在實踐中,可能需要把緩沖區(qū)分為塊,并針對塊操作而非單個字節(jié)。緩沖區(qū)的大小也是一個必須仔細選擇的參數(shù),需要基于實驗。

總結

以上是生活随笔為你收集整理的Qt之线程同步(生产者消费者模式 - QWaitCondition)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。