天天看點

QThread與QWidget使用

本文主要内容:

在任務一中,用 四

種方式實作:點選界面按鈕,開線程運作一段程式,結果顯示在一個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在次線程中

恩。這篇文字似乎又不短了,看來任務二要另起一篇了。