QT---多線程


在Qt中使用線程比較簡單,只需要繼承QThread類並重新實現其run()函數,代碼如下

class MyThread : public QThread
{
  Q_OBJECT
projected:
  void run();    
};

void MyThread :: run()
{
 .......
}

只需在run()函數中填寫所需要的功能代碼,然后創建一個MyThread實例,並以QThread::start()函數啟動這個實例就可以了。這樣run()函數中的功能代碼就運行在一個獨立的線程中。

 

多線程程序的特點:

1. 多線程程序的行為無法預測,當我們多次執行同一個程序時,每次運行的結果都不相同;

2. 線程的執行順序無法保證,它與操作系統的調度策略和線程優先級等因素有關;

3. 線程的切換可能發生在任何時刻任何地點;

4. 線程對代碼的敏感高,代碼的細微修改可能產生意向不到的結果;

為了有效的使用線程,必須對其進行控制。

 

線程間的關系:互斥關系和同步關系

QT提供的幾個類: QMutex、 QmutexLocker、QReadWriteLocker、QReadLocker、QWriteLocker、QSemaphore、QWaitConditon

 

QMutex類提供一個保護一段臨界區代碼的方法,它每次只允許一個線程訪問這段臨界區代碼

QMutex類的lock函數用來鎖住互斥量,如果互斥量處於解鎖狀態,當前線程就會立即抓住並鎖定它;否則當前線程就會被阻塞,直到持有這個互斥量的線程對它解鎖。線程調用lock函數后就會一直持有這個互斥量知道再次調用unlock函數。  也就是說 lock和unlock函數是成對出現的。這就出現一個問題,如果說你的功能代碼里需要返回一個數據比如:

{

mutex.lock();

............

............


return value;

mutex.unlock();

}

上面的代碼顯然會有一個問題:return 后的代碼是不是執行的,這就會出現這個線程不會釋放乇鶚互斥量導致其他行程無法獲得互斥量而一直處於阻塞狀態;

Qt給出了解決方法,QMutexLocker類可以簡化互斥量的處理

The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes.

Locking and unlocking a QMutex in complex functions and statements or in exception handling code is error-prone and difficult to debug. QMutexLocker can be used in such situations to ensure that the state of the mutex is always well-defined.

QMutexLocker should be created within a function where a QMutex needs to be locked. The mutex is locked when QMutexLocker is created. You can unlock and relock the mutex with unlock() and relock(). If locked, the mutex will be unlocked when the QMutexLocker is destroyed.

構造函數是: QMutexLocker ( QMutex * mutex ),在構造函數中接受一個QMutex對象作為參數並將其鎖定,在析構函數中解鎖這個互斥量。

那么上面有點問題的代碼就可以改成如下:

{

QMutexLocker locker(&mutex);

............

............


return value;

}

上面的locker作為局部變量會在數據退出的時候結束其作用域,從而自動調用析構函數來對互斥量mutex解鎖。

 

QSemaphore

信號量對互斥量做了擴充,互斥量只能鎖定一次而信號量可以獲取多次。acquire()函數用於獲取n個資源,當沒有足夠的資源時調用者會被阻塞,直到有足夠的可用資源,release()函數用於釋放n個資源。

 

生產者/消費者的問題中有兩處關鍵:如果生產者過快地生產數據,將會覆蓋消費者還沒有讀取的數據;如果消費者過快地讀取數據,將越過生產者並且讀取到一些過期數據。

兩個關鍵的兩個處理方法:

1.先讓生產者填滿整個緩沖區,然后等消費者讀取整個消費區;

2.讓生產者和消費者同時分別操作緩沖區的不同部分,需要兩個信號量;

對於方法2,比如:

QSemaphore freeBytes(BufferSize);

QSemaphore usedBytes(0);

freeBytes信號量控制可被生產者填充的緩沖區部分,usedBytes信號量控制可被消費者讀取的緩沖區部分。這兩個信號量互為補充的。其中freeBytes信號量被初始化為最大,usedBytes被初始化為0

例程見QT自帶例程的examples\threads\semaphores

 

除了互斥與同步控制,多線程編程還涉及死鎖和優先級控制兩個經典話題。

Linux中的線程調度策略:

SCHED_FIFO  先進先出

SCHED_RR   輪轉

SCHED_OTHER  其他

死鎖和優先級反轉,兩者在很大程度上都是因為線程的互斥與同步操作不當引起的。

死鎖的產生必須具備以下4個條件:

1.互斥條件

2.請求保護條件

3.不可剝奪條件

4.環路等待條件

 

在具有圖形用戶界面的Qt應用程序中,主線程由GUI線程充當,該線程是Qt中唯一可以執行GUI相關操作的線程,即一個具有圖形用戶界面的Qt應用程序只能有一個GUI線程。但卻可以同時擁有一個或多個GUI線程為工作線程處理其他耗時操作。

 

這里拋出一個問題:非GUI線程怎么和GUI線程通訊?

 

可重入(reentrant):如果一個啊哈念書能同事被多個線程調用,並且為每一個調用者提供一份單獨的數據,那么稱這個函數是可重入的。

線程安全(thread-safe):如果一個函數能同事被多個線程調用,並且所有的調用者引用同一份數據,訪問數據時串行處理,那么稱這個函數是線程安全的。

 

GUI線程是唯一允許創建QApplication對象並且對它調用exec()函數的線程。在調用了exec()函數后,GUI線程要么等待一個事件,要么處理一個事件。每一個線程都有自己的事件循環。

起始線程通過QCoreApplication::exec()函數啟動事件循環,其他非GUI線程通過QThread::exec()啟動各自的事件循環。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM