QT學習(五)QT之多執行緒
1. 執行緒同時進行
QT提供了QThread來定義一個執行緒,我們通過定義類thread來重新實現它。
classThread:publicQThread
{
Q_OBJECT
public:
Thread();
voidsetMessage(constQString&Message);
voidstop();
protected:
private:
QStringMessageStr;
volatileboolstopped;
};
執行緒通過判斷stopped變數來選擇列印資訊,重新實現了run函式和stop函式,用於執行緒執行和終止。
voidThread::run(){
while(!stopped)
std::cerr<<qPrintable(MessageStr);
stopped=false;
std::cerr<<std::endl;
}
voidThread::stop(){
stopped=true;
}
接下來製作一個簡單的對話方塊,實現兩個按鈕,可以呼叫執行緒。這兩個執行緒可以同時執行,互不相關,有各自的stopped判別變數。
public:
threadDialog(QWidget*parent=0);
privateslots:
voidstartOrStopThreadA();
voidstartOrStopThreadB();
protected:
voidcloseEvent(QCloseEvent*closeEvent);
private:
ThreadthreadA;
ThreadthreadB;
QPushButton*threadButtonA;
QPushButton*threadButtonB;
QPushButton*quitButton;
這是類的定義,包括定義了三個按鈕,分別用於啟動執行緒A和B以及停止兩個執行緒的按鈕。
當同時按下A 和B執行緒的按鈕,會交替輸出A和B的資訊。
2. 互斥鎖
QT通過QMutex來建立保護多執行緒共同訪問區,通過lock和unlock分別來鎖住和解鎖一段變數或者程式碼。當引用lock時,其後邊的程式碼都被當前程序佔用,其他程序不能訪問,直到呼叫unlock後,其他程式才可以呼叫。上面程式碼可以修改為:
voidThread::run(){
forever{
mutex.lock();
if(stopped){
stopped=false;
mutex.unlock();
break;
}
mutex.unlock();
std::cerr<<qPrintable(MessageStr);
}
std::cerr<<std::endl;
}
voidThread::stop(){
mutex.lock();
stopped=true;
mutex.unlock();
}
3. 訊號量
訊號量可以用於當兩個執行緒之間傳遞大量資料時,兩個執行緒通過訊號量來確定緩衝區大小,以及自己是否可以進行讀寫操作。讀寫緩衝區是典型的生產者和消費者型別,生產者向緩衝區寫資料,消費者讀取資料。現在定義兩個訊號量,一個檢測緩衝區可以寫的空間大小,一個記錄可以讀的空間大小,這類似於FIFO。
QSemaphorefreeSpace(BufferSize);
QSemaphoreusedSpace(0);
訊號量通過acquire來獲得可用區域,而通過release來釋放已經完成的區域。
voidProducer::run()
{
for(inti=0;i<DataSize;++i){
freeSpace.acquire();
buffer[i%BufferSize]="ACGT"[uint(std::rand())%4];
usedSpace.release();
}
}
voidConsumer::run()
{
for(inti=0;i<DataSize;++i){
usedSpace.acquire();
std::cerr<<buffer[i%BufferSize];
freeSpace.release();
}
std::cerr<<std::endl;
}
生產者每次寫入一個數據,usedSpace就增加一個可讀資料。而消費者每次讀一個數據,freeSpace就增加一個自由空間。
4. 等待條件
除了使用訊號量,還可以通過QWaitCondition定義等待變數,用於執行緒開啟和等待。
QWaitConditionbufferIsNotFull;
QWaitConditionbufferIsNotEmpty;
QMutexmutex;
intusedSpace=0;
以上bufferIsNotFull和bufferIsNotEmpty可以來喚起和阻塞程序。而usedSpace則用於計算可用空間。
voidProducer::run()
{
for(inti=0;i<DataSize;++i){
mutex.lock();
while(usedSpace==BufferSize)
bufferIsNotFull.wait(&mutex);
buffer[i%BufferSize]="ACGT"[uint(std::rand())%4];
++usedSpace;
bufferIsNotEmpty.wakeAll();
mutex.unlock();
}
}
voidConsumer::run()
{
for(inti=0;i<DataSize;++i){
mutex.lock();
while(usedSpace==0)
bufferIsNotEmpty.wait(&mutex);
std::cerr<<buffer[i%BufferSize];
--usedSpace;
bufferIsNotFull.wakeAll();
mutex.unlock();
}
std::cerr<<std::endl;
}
生產者如果檢測到空間已經寫滿,就會被bufferIsNotFull阻塞等待,直到消費者通過消費空間,使得usedSpace不滿為止。然後生產者寫入一個數據,可用空間就增加一個。消費者正好與生產者相反。