QT学习:多线程控制
實現線程的互斥與同步常使用的類有QMutex、QMutexLocker、QReadWriteLocker、QReadLocker、
QWriteLocker、QSemaphore和QWaitCondition。
下面舉一個例子加以說明:
在多線程環境下,這個類是不安全的,因為存在多個線程同時修改私有成員key,其結果是不可預知
的。
雖然Key類產生主鍵的函數creatKey()只有一條語句執行修改成員變量key的值,但是C++的“++”操作符
并不是原子操作,通常編譯后,它將被展開成為以下三條機器命令:
(1)將變量值載入寄存器。
(2)將寄存器中的值加1。
(3)將寄存器中的值寫回主存。
一、互斥量
1、QMutex類
QMutex類的lock()函數用于鎖住互斥量。如果互斥量處于解鎖狀態,則當前線程就會立即抓住并鎖定它,否則當前線程就會被阻塞,直到持有這個互斥量的線程對它解鎖。線程調用lock()函數后就會持有這個互斥量,直到調用unlock()操作為止。 QMutex類還提供了一個tryLock()函數。如果互斥量已被鎖定,則立即返回。 例如:
class Key { public: Key() {key=0;} int creatKey() { mutex.lock(); ++key; return key; mutex. unlock();} int value()const { mutex.lock(); return key; mutex.unlock();} private: int key; QMutex mutex; };2、QMutexLocker類
Qt提供的QMutexLocker類可以簡化互斥量的處理,它在構造函數中接收一個QMutex對象作為參數并將其鎖定,在析構函數中解鎖這個互斥量,這樣就解決了以上問題。
例如:
locker()函數作為局部變量會在函數退出時結束其作用域,從而自動對互斥量mutex解鎖。
二、信號量
信號量可以理解為對互斥量功能的擴展,互斥量只能鎖定一次而信號量可以獲取多次,它可以用來保護一定數量的同種資源。信號量的典型用例是控制生產者/消費者之間共享的環形緩沖區。 生產者/消費者實例中對同步的需求有兩處:
(1)如果生產者過快地生產數據,將會覆蓋消費者還沒有讀取的數據。
(2)如果消費者過快地讀取數據,將越過生產者并且讀取到一些過期數據。
針對以上問題,有兩種解決方法:
(1)首先使生產者填滿整個緩沖區,然后等待消費者讀取整個緩沖區,這是一種比較笨拙的方 法。
(2)使生產者和消費者線程同時分別操作緩沖區的不同部分,這是一種比較高效的方法。
具體使用方法見如下代碼:
(1)在源文件“main.cpp”中添加的具體實現代碼如下:
(2)Producer類繼承自QThread類,作為生產者類,其聲明如下:
class Producer : public QThread { public: Producer(); void run(); };Producer構造函數中沒有實現任何內容:
Producer::Producer() { }Producer::run()函數的具體實現代碼如下:
void Producer::run() { for(int i=0;i<DataSize;i++) { freeBytes.acquire(); //生產者線程首先獲取一個空閑單元,如果此時緩沖區被消費者尚未讀取的數據填滿,對此函數的調用就會阻塞,直到消費者讀取了這些數據為止。 buffer[i%BufferSize]=(i%BufferSize); //一旦生產者獲取了某個空閑單元,就使用當前的緩沖區單元序號填寫這個緩沖區單元。 usedBytes.release(); //調用該函數將可用資源加1,表示消費者此時可以讀取這個剛剛填寫的單元了。 } }release(n)函數用于釋放n個資源。
(3)Consumer類繼承自QThread類,作為消費者類,其聲明如下:
Consumer構造函數中沒有實現任何內容:
Consumer::Consumer() { }Consumer::run()函數的具體實現代碼如下:
void Consumer::run() { for(int i=0;i<DataSize;i++) { usedBytes.acquire(); //消費者線程首先獲取一個可被讀取的單元,如果緩沖區中沒有包含任何可以讀取的數據,對此函數的調用就會阻塞,直到生產者生產了一些數據為止。fprintf(stderr,"%d",buffer[i%BufferSize]); //一旦消費者獲取了這個單元,會將這個單元的內容打印出來。 if(i%16==0&&i!=0) fprintf(stderr,"\n"); freeBytes.release(); //調用該函數使得這個單元變為空閑,以備生產者下次填充。 }fprintf(stderr,"\n"); }(4)main()函數的具體內容如下:
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Producer producer; Consumer consumer; /* 啟動生產者和消費者線程 */ producer.start(); consumer.start(); /* 等待生產者和消費者各自執行完畢后自動退出 */ producer.wait(); consumer.wait(); return a.exec(); }(5)最終運行結果如下圖所示:
總結
以上是生活随笔為你收集整理的QT学习:多线程控制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT学习:多线程
- 下一篇: QT学习:线程等待与唤醒