在之前的文章中,我們已經講過了很多種線程同步的方法,如互斥鎖,信號量,讀寫鎖等,今天我們再來學習一種線程同步的方法,條件變量。
條件變量允許一個線程通知其他的線程它們所等待的某個條件已經滿足了,可以繼續運行了。一個或多個線程可以在同一個條件變量上等待。當條件滿足時,我們可以調用wakeOne()從所有等待在該條件變量上的線程中隨機的喚醒一個線程繼續運行,也可以使用wakeAll()方法同時喚醒所有等待在該條件變量上的線程。
另外,QWaitCondition和QSemaphore一樣,要和QMutex配合使用,因爲要訪問到共享資源。
下面,我們使用常見的生產者-消費者問題來演示一下條件變量的使用。
新建一個Qt控制檯程序,再新建兩個線程類Producer和Consumer,繼承自QThread類。
我們先在main.cpp中,聲明我們要用到的全局變量。代碼如下:
#include <QCoreApplication>
#include <QWaitCondition>
#include <QMutex>
#include <QQueue>
QQueue<int> buffer;
QMutex mutex;
QWaitCondition fullCond;
QWaitCondition emptyCond;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
return a.exec();
}
其中,buffer就是我們的緩衝區,mutex是保護緩存區的互斥鎖,條件變量fullCond是用來等待緩衝區變滿(有數據),條件變量emptyCond是用來等待緩衝區變爲空。
下面來實現我們的消費者和生產者線程:
生產者代碼如下:
void Producer::run()
{
while(true)
{
mutex.lock();
while(buffer.size() >= 10)
{
emptyCond.wait(&mutex);
}
int num = rand();
buffer.enqueue(num);
qDebug() << "enqueue: " << num;
mutex.unlock();
fullCond.wakeAll();
}
}
此處,我們假定緩衝區最多存儲10個元素。所以,我們先判斷緩衝區是否已滿,如果已滿,則等待其變爲空,否則,產生一個隨機數放入隊列中,最後通知消費者線程,可以進行消費。
消費者代碼如下:
void Consumer::run()
{
while(true)
{
mutex.lock();
while(buffer.size() <= 0)
{
fullCond.wait(&mutex);
}
qDebug() << "dequeue: " << buffer.dequeue();
mutex.unlock();
emptyCond.wakeAll();
}
}
對於消費者來說,要先判斷緩衝區是否有數據可消費,如果沒有,則等待其生產者生產出新的數據,如果有,則消費一個數據,最後,通知生產者繼續生產。
運行結果如下:
可以看出,幾乎是生產者生產一個數據,消費者就消費一個。如果想看到累積的效果,可以使用線程睡眠來實現,大家可以自行測試。