本文主要内容:
在任務一中,用 四
種方式實作:點選界面按鈕,開線程運作一段程式,結果顯示在一個Label上。
1.
用不正确的方式得到看似正确的結果
2. 用Qt Manual 和 例子中使用的方法
3.
用一種好用但被Qt開發人員批判的方法
4. 用一種被開發人員強烈推薦,但Qt
Manual和例子中隻字未提的方法
為了簡單起見,本文隻講如何做及其結果是什麼,而不講其原因是什麼(估計大家對原因也不會感興趣,詳見: 和 )。
本文隻考慮兩個線程(即主線程和一個次線程)的情況。
QWidget及其派生類均 不能在次線程中使用或建立
Manual 中的原話:
The GUI classes, notably QWidget and all its subclasses, are not
reentrant. They can only be used from the main thread.
因為不允許,是以嘗試這麼做的,幾乎很快都能回頭。畢竟signals和slots用起來确實蠻友善
但是,回頭後,就了解和用對 QThread 了麼?
概念一:QThread 對象本身所依附的線程 和它管理的線程不是同一個線程。
前者是主線程
後者是次線程
概念二:你在QThread派生類中定義的槽是在主線程而不是在次線程中執行的。
run 函數是線程的入口點,run内的代碼才是在次線程中運作的代碼
概念三:除了Manual和Qt例子中給出的用法外,QThread有一種更容易且被推薦的使用方法:
QThread 應該被看做是作業系統線程的接口或控制點,而不應該包含需要在新線程中運作的代碼
需要運作的代碼應該放到一個QObject的子類中,然後将該子類的對象moveToThread到新線程中。
為了代碼簡單,所有例子都是單一的源檔案,儲存為 main.cpp
你從代碼中包含的 #include“main.moc”應該能看出
為了省幾行代碼,頭檔案都是直接包含 QtCore 和 QtGui。
為了清楚告訴大家槽函數分别是在那個線程運作的,調用了幾處 currentThreadId 函數
main
函數中輸出主線程IDqDebug()<<"main: "<<QThread::currentThreadId();
run
函數中輸出次線程IDqDebug()<<"thread: "<<currentThreadId();
槽函數中輸出其在哪個線程中執行qDebug()<<"slots1: "<<currentThreadId();
因為用了qDebug,是以你的pro檔案内最好加上 CONFIG+=console
同樣為了省代碼,例子中未考慮線程如何正常結束的問題。
點選界面按鈕,開線程運作一段程式,結果顯示在一個Label上。
定義一個Widget,上面放置 QPushButton 和 QLabel
定義一個Thread,執行我們的代碼,然後通知 Widget
很容易想到方法,代碼可以工作,結果正确。但 ... 未必和你想得一樣
Thread 中定義一個slot1函數,接受資料,計算其立方,然後将結果通過信号發出
Widget 中按鈕每點選一次,發出的資料加1,用label接受Thread的信号
一切工作正常,但看看控制台輸出呢?
這兒明确告訴你,slot1 是在主線程中執行的。
我們試試 Qt Manual和 Qt 例子中采用的解決方案。
槽函數不是在主線程運作麼,而run函數不是次線程麼?那麼我們就:
在槽函數中做個标記
在run函數中根據标記進行運作
這樣以來,盡管槽函數在仍在主線程,但費時的計算代碼都在次線程了。
對Thread的類的改造如下(程式其他部分和 嘗試一 完全一樣):
注意哦,因為 slot 函數在主線程中,而run函數在次線程中,是以二者需要 QMutex
實作對變量的安全通路。如果你認真看過Qt自帶的例子,會發現它始終強調 QMutex 的使用。
嘗試二是"正統"的做法,但如過你用Google搜尋過。那麼你可能不會選擇嘗試二,而是會使用下面的方法(其他部分和嘗試一 完全一樣)
這樣以來,slot函數确實是在次線程工作的,看看控制台輸出
很有意思?不是麼,一條 moveToThread(this),移動到自己。然後問題解決了。
因為前面說了,QThread 所依附線程 和 它管理的線程不是同一個。
這樣,其實将自己所依附的線程改為自己所管理的線程了。
o(∩∩)o...哈哈,不要太高興哦,這個方法看起來比較舒服,但是它是被官方人員強烈批判的用法
終于到我想寫的代碼了,這是Qt線程的開發者建議的使用方式,但很可惜。直到目前(Qt4.7.0),手冊和例子中對此都隻字為提。
我們不子類話QThread了,我們隻需要子類話一個QObject,然後将其move到QThread就行了,看代碼:
不用子類化QThrad了,我們隻需要子類話一個 QObject,需要在次線程中工作的代碼,直接放到它的槽中
因為沒有Thread類了,隻有Worker類,Widget代碼需做點改動
main 函數還是和嘗試一完全一樣
控制台輸出結果如下
一共兩個線程,且二者id不同,說明slot在次線程中
恩。這篇文字似乎又不短了,看來任務二要另起一篇了。