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