回顧Qt之線程(QThread),里面講解了如何使用線程,但還有很多人留言沒有看明白,那么今天我們來一起瞅瞅關於QThread管理線程的那些事兒。。。
1、線程啟動
void start(Priority priority = InheritPriority)
調用后會執行run()函數,但在run()函數執行前會發射信號started(),操作系統將根據優先級參數調度線程。如果線程已經在運行,那么這個函數什么也不做。優先級參數的效果取決於操作系統的調度策略。特別是那些不支持線程優先級的系統優先級將會被忽略(例如在Linux中,更多細節請參考http://linux.die.net/man/2/sched_setscheduler)。
2、線程執行
int exec()
進入事件循環並等待直到調用exit(),返回值是通過調用exit()來獲得,如果調用成功則范圍0。
virtual void run();
線程的起點,在調用start()之后,新創建的線程就會調用這個函數,默認實現調用exec(),大多數需要重新實現這個功能,便於管理自己的線程。該方法返回時,該線程的執行將結束。
3、線程退出
void quit()
告訴線程事件循環退出,返回0表示成功,相當於調用了QThread::exit(0)。
void exit(int returnCode = 0)
告訴線程事件循環退出。
調用這個函數后,線程離開事件循環后返回,QEventLoop::exec()返回returnCode,
按照慣例0表示成功,任何非0值表示失敗。
void terminate()
終止線程,線程可能會立即被終止也可能不會,這取決於操作系統的調度策略,使用terminate()之后再使用QThread::wait()確保萬無一失。
當線程被終止后,所有等待中的線程將會被喚醒。
警告:此功能比較危險,不鼓勵使用。線程可以在代碼執行的任何點被終止。線程可能在更新數據時被終止,從而沒有機會來清理自己,解鎖等等。。。總之,只有在絕對必要時使用此功能。
建議:一般情況下,都在run函數里面設置一個標識符,可以控制循環停止。然后才調用quit函數,退出線程。
4、線程等待
void msleep(unsigned long msecs)
強制當前線程睡眠msecs毫秒
void sleep(unsigned long secs)
強制當前線程睡眠secs秒
void usleep(unsigned long usecs)
強制當前線程睡眠usecs微秒
bool wait(unsigned long time = ULONG_MAX);
線程將會被阻塞,等待time毫秒。和sleep不同的是,如果線程退出,wait會返回。
5、線程狀態
bool isFinished() const
線程是否結束
6、線程優先級
Constant |
Value |
Description |
QThread::IdlePriority |
0 |
沒有其它線程運行時才調度. |
QThread::LowestPriority |
1 |
比LowPriority調度頻率低. |
QThread::LowPriority |
2 |
比NormalPriority調度頻率低. |
QThread::NormalPriority |
3 |
操作系統默認的默認優先級. |
QThread::HighPriority |
4 |
比NormalPriority調度頻繁. |
QThread::HighestPriority |
5 |
比HighPriority調度頻繁. |
QThread::TimeCriticalPriority |
6 |
盡可能頻繁的調度. |
QThread::InheritPriority |
7 |
使用和創建線程同樣的優先級. 這是默認值. |
在Qt之線程(QThread)一節中我介紹了QThread 的兩種使用方法:
1、子類化 QThread(不使用事件循環)。
這是官方手冊、例子以及相關書籍中都介紹的一種常用的方法。
a. 子類化 QThread
b. 重載 run 函數,run函數內有一個while或for的死循環(模擬耗時操作)
c. 設置一個標記為來控制死循環的退出。
2、子類化 QObject
a. 子類化 QObject
b. 定義槽函數
c. 將該子類的對象moveToThread到新線程中
run 對於線程的作用相當於main函數對於應用程序。它是線程的入口,run的開始和結束意味着線程的開始和結束。
采用這兩種做法,毫無疑問都會在次線程中運行(這里說的是,run中的邏輯以及子類化QObject后連接通過moveToThread然后連接到QThread的started()信號的槽函數,這個下面會詳細講解)。
那么,線程中的槽函數是怎么運行的呢?
說到信號與槽,大家應該再熟悉不過了,包括我,特別喜歡使用自定義信號與槽,感覺用起來特方便、特棒。。。
經常使用,你能否100%的使用正確?你了解它的高級用法嗎?
1、你是否在多次connect,還發現不了為什么槽函數會執行那N多次。
2、你是否了解disconnect
3、你是否了解connect中的第五個參數 Qt::ConnectionType
關於connect、disconnect信號、槽的使用可參考:Qt之信號與槽。既然談到線程這里需要重點說下Qt::ConnectionType(信號與槽的傳遞方式)
Constant |
Value |
Description |
Qt::AutoConnection |
0 |
自動連接:(默認值)如果信號在接收者所依附的線程內發射,則等同於直接連接。如果發射信號的線程和接受者所依附的線程不同,則等同於隊列連接。 |
Qt::DirectConnection |
1 |
直接連接:當信號發射時,槽函數將直接被調用。無論槽函數所屬對象在哪個線程,槽函數都在發射信號的線程內執行。 |
Qt::QueuedConnection |
2 |
隊列連接:當控制權回到接受者所依附線程的事件循環時,槽函數被調用。槽函數在接收者所依附線程執行。也就是說:這種方式既可以在線程內傳遞消息,也可以跨線程傳遞消息 |
Qt::BlockingQueuedConnection |
3 |
與Qt::QueuedConnection類似,但是會阻塞等到關聯的slot都被執行。這里出現了阻塞這個詞,說明它是專門用來多線程間傳遞消息的。 |
舉例:
MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0);
public slots:
void start();
};
#endif // MYOBJECT_H
MyObject.cpp
#include "MyObject.h"
#include
#include
MyObject::MyObject(QObject *parent)
: QObject(parent)
{
}
void MyObject::start()
{
qDebug() << QString("my object thread id:") << QThread::currentThreadId();
}
main.cpp
#include "MyObject.h"
#include
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug() << QString("main thread id:") << QThread::currentThreadId();
MyObject object;
QThread thread;
object.moveToThread(&thread);
QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()));
thread.start();
return a.exec();
}
"main thread id:" 0xf08
"my object thread id:" 0x216c
顯然主線程與槽函數的線程是不同的(你可以多次嘗試,屢試不爽。。。),因為moveToThread后MyObject所在的線程為QThread,繼上面介紹的thread.start()執行后首先會發射started()信號,也就是說started()信號發射是在次線程中進行的,所以無論采取Qt::AutoConnection、Qt::DirectConnection、Qt::QueuedConnection哪種連接方式,主線程與槽函數的線程都是不同的。
1、修改代碼如下:
MyObject object;
QThread thread;
//object.moveToThread(&thread);
QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()), Qt::DirectConnection);
thread.start();
"main thread id:" 0x2688
"my object thread id:" 0x2110
顯然主線程與槽函數的線程是不同的,MyObject所依附的線程為主線程(因為注釋掉了moveToThread),繼上面介紹的Qt::DirectConnection(無論槽函數所屬對象在哪個線程,槽函數都在發射信號的線程內執行)。也就是說started()信號發射是在次線程中進行的,槽函數也是在次線程中進行的,所以主線程與槽函數的線程是不同的。
2、修改代碼如下:
MyObject object;
QThread thread;
//object.moveToThread(&thread);
QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()), Qt::QueuedConnection);
thread.start();
查看運行結果:
"main thread id:" 0x24ec
"my object thread id:" 0x24ec
顯然主線程與槽函數的線程是相同的,繼上面介紹的Qt::QueuedConnection(槽函數在接收者所依附線程執行)。也就是說started()信號發射是在次線程中進行的,但MyObject所依附的線程為主線程(因為注釋掉了moveToThread),所以主線程與槽函數的線程必然是相同的。
3、修改代碼如下:
MyObject object;
QThread thread;
//object.moveToThread(&thread);
QObject::connect(&thread, SIGNAL(started()), &object, SLOT(start()), Qt::AutoConnection);
thread.start();
"main thread id:" 0x2700
"my object thread id:" 0x2700
顯然主線程與槽函數的線程是相同的,MyObject所依附的線程為主線程(因為注釋掉了moveToThread),繼上面介紹的Qt::AutoConnection(如果信號在接收者所依附的線程內發射,則等同於直接連接。如果發射信號的線程和接受者所依附的線程不同,則等同於隊列連接。)。因為started()信號和MyObject依附的線程不同,所以結果和Qt::QueuedConnection對應的相同,所以主線程與槽函數的線程是相同的。
基本就介紹到這里,QThread使用和上面的大同小異,run里面執行的代碼都是在次線程中,如果是QThead的槽函數,那么結論同上!