Qt之线程同步(生产者消费者模式 - QSemaphore)
簡述
生產者將數據寫入緩沖區,直到它到達緩沖區的末尾,此時,它將從開始位置重新啟動,覆蓋現有數據。消費者線程讀取數據并將其寫入標準錯誤。
Semaphore(信號量) 比 mutex(互斥量)有一個更高級的并發性。如果緩沖區的訪問由一個 QMutex 把守,當生產者線程訪問緩沖區時,消費者線程將無法訪問。然而,有兩個線程同一時間訪問不同的緩沖區是沒有害處的。
示例包括兩個類:Producer 和 Consumer,均繼承自 QThread。循環緩沖區用于這兩個類之間的溝通,信號量用于保護全局變量。
另一種實現“生產者 - 消費者”模式的方案是使用 QWaitCondition 和 QMutex - Qt之線程同步(生產者消費者模式 - QWaitCondition)
- 簡述
- 全局變量
- Producer
- Consumer
- main 函數
- 更多參考
全局變量
首先,一起來看循環緩沖區和相關的信號量:
const int DataSize = 100000;const int BufferSize = 8192; char buffer[BufferSize];QSemaphore freeBytes(BufferSize); QSemaphore usedBytes;DataSize 是生產者將生成的數據數量,為了讓示例盡可能地簡單,把它定義為一個常數。BufferSize 是循環緩沖區的大小,小于 DataSize,這意味著在某一時刻生產者將達到緩沖區的末尾,會從開始位置重新啟動。
要同步生產者和消費者,需要兩個信號量。freeBytes 信號量用于控制緩沖區的 "free" 區域(生產者尚未填充數據,或消費者已經讀取的區域)。usedBytes 信號量用于控制緩沖區的 "used" 區域(生產者已經填充數據,但消費者尚未讀取的區域)。
總之,這些信號量確保生產者不會先于消費者超過 BufferSize 的大小,而消費者永遠不會讀取生產者尚未生成的數據。
freeBytes 信號量用 BufferSize 來初始化,因為最初整個緩沖區是空的。usedBytes 信號量初始化為 0(默認值,如果未指定)。
Producer
Producer 類的代碼如下:
class Producer : public QThread { public:void run() Q_DECL_OVERRIDE{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();}} };生產者生成 DataSize 字節的數據。在往循環緩沖區寫入一個字節之前,它必須使用 freeBytes 信號量來獲得一個 "free" 字節。如果消費者沒有與生產者保持著同樣的速度,QSemaphore::acquire() 調用可能會阻塞。
最后,生產者使用 usedBytes 信號量釋放一個字節。該 "free" 字節已成功轉化為一個 "used" 字節,準備好供消費者讀取。
Consumer
現在轉向 Consumer 類:
class Consumer : public QThread {Q_OBJECT public:void run() Q_DECL_OVERRIDE{for (int i = 0; i < DataSize; ++i) {usedBytes.acquire();fprintf(stderr, "%c", buffer[i % BufferSize]);freeBytes.release();}fprintf(stderr, "\n");}signals:void stringConsumed(const QString &text);protected:bool finish; };代碼非常類似于生產者,不同的是,獲得 "used" 字節并釋放一個 "free" 字節,而非相反。
main() 函數
在 main() 函數中,我們創建兩個線程,并調用 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; }當運行這個程序時,會發生什么呢?
最初,生產者是唯一一個可以做任何事情的線程,消費者阻塞并等待 usedBytes 信號量的釋放(available() 初始數是 0)。一旦生產者把一個字節放入緩沖區,freeBytes.available() 就會變為 BufferSize - 1,并且 usedBytes.available() 變為 1。這時,可能發生兩件事:要么消費者線程接管和讀取字節,要么生產者開始生成第二個字節。
此示例中提出的“生產者 - 消費者”模式,適用于編寫高并發多線程應用。在多處理器計算機中,程序可能比基于 mutex 的方案快達兩倍之多,因為兩個線程可以同一時間在緩沖區的不同部分處于激活狀態。
要知道,這些好處并不總能實現,獲取和釋放一個 QSemaphore 是需要成本的。在實踐中,可能需要把緩沖區分為塊,并針對塊操作而非單個字節。緩沖區的大小也是一個必須仔細選擇的參數,需要基于實驗。
總結
以上是生活随笔為你收集整理的Qt之线程同步(生产者消费者模式 - QSemaphore)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【bzoj3879】SvT 后缀数组+
- 下一篇: Kotlin 文档 .Google 正式