Qt線程QThread開(kāi)啟和安全退出的實(shí)現(xiàn)
1、線程開(kāi)啟
Qt中,開(kāi)啟子線程,一般有兩種方法:
a, 定義工作類worker:
worker繼承 QThread, 重寫(xiě)run函數(shù),在主線程中實(shí)例化worker,把耗時(shí)工作放進(jìn)worker的run函數(shù)中完成,結(jié)束后,往主線程中發(fā)信號(hào),傳遞參數(shù)即可。
注意:此worker的實(shí)例,只有run函數(shù)在子線程中執(zhí)行,worker的其他函數(shù),均在主線程中執(zhí)行。
如果子線程已經(jīng)start開(kāi)啟,run函數(shù)尚未運(yùn)行完時(shí),再次start,此時(shí)子線程不會(huì)有任何操作,run函數(shù)不會(huì)被重新調(diào)用,會(huì)繼續(xù)執(zhí)行run函數(shù)。
b, 定義工作類worker:
worker繼承Qobject,在worker中完成耗時(shí)操作,并在主線程中 #include “worker.h”進(jìn)來(lái),隨后,在主線程中New出幾個(gè)子線程QThread,使用moveToThread()函數(shù),把worker轉(zhuǎn)移進(jìn)入子線程,例如:
pWorker = new Worker; pThread1 = new QThread; pWorker->moveToThread(pThread1)
之后,再根據(jù)需求,看何時(shí)開(kāi)啟子線程:pThread1->start();
如果線程已經(jīng)運(yùn)行,你重復(fù)調(diào)用start其實(shí)是不會(huì)進(jìn)行任何處理。
2、線程關(guān)閉
對(duì)于上面a類,在run中開(kāi)啟的子線程,如果run中沒(méi)有調(diào)用exec(),使用quit(),exit(),是無(wú)法跳出run中的循環(huán),終止子線程的。不會(huì)發(fā)生任何效果,QThread不會(huì)因?yàn)槟阏{(diào)用quit()函數(shù)而退出正在運(yùn)行到一半的run。
但使用QThread的terminate()方法,可以立刻結(jié)束子線程,但這個(gè)函數(shù)存在非常不安定因素,不推薦使用。那么如何安全的終止一個(gè)線程呢?
最簡(jiǎn)單的方法是添加一個(gè)bool變量,通過(guò)主線程修改這個(gè)bool變量來(lái)進(jìn)行終止,但這樣有可能引起訪問(wèn)沖突,需要對(duì)其進(jìn)行加鎖。
void myThread::run()
{
int count = 0;
m_isCanRun = true;//標(biāo)記可以運(yùn)行
QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__)
.arg((unsigned int)QThread::currentThreadId());
emit message(str);
while(1)
{
sleep(1);
++count;
doSomething();
if(m_runCount == count)
{
break;
}
{
QMutexLocker locker(&m_lock);// 此處加鎖,防止訪問(wèn)沖突
if(!m_isCanRun)//在每次循環(huán)判斷是否可以運(yùn)行,如果不行就退出循環(huán)
{
return;
}
}
}
}因此在子線程的run函數(shù)的循環(huán)中遇到m_isCanRun的判斷后就會(huì)退出run函數(shù),繼承QThread的函數(shù)在運(yùn)行完run函數(shù)后就視為線程完成,會(huì)發(fā)射finish信號(hào)。
子線程指針,盡量不要去delete ,這樣不安全。一般會(huì)綁定QObject::deleteLater()方法。
connect(pThread,&QThread::finished ,thread,&QObject::deleteLater);
線程結(jié)束后調(diào)用deleteLater來(lái)銷(xiāo)毀分配的內(nèi)存。
對(duì)于上面b類,在Qt4.8之后,Qt多線程的寫(xiě)法最好還是通過(guò)QObject來(lái)實(shí)現(xiàn),和線程的交互通過(guò)信號(hào)和槽(實(shí)際上其實(shí)是通過(guò)事件)聯(lián)系。
繼承QObject多線程的方法線程的創(chuàng)建很簡(jiǎn)單,只要讓QThread的start函數(shù)運(yùn)行起來(lái)就行,但是需要注意銷(xiāo)毀線程的方法: 在線程創(chuàng)建之后,這個(gè)QObject的銷(xiāo)毀不應(yīng)該在主線程里進(jìn)行,而是通過(guò)deleteLater槽進(jìn)行安全的銷(xiāo)毀,因此,繼承QObject多線程的方法在創(chuàng)建時(shí)有幾個(gè)槽函數(shù)需要特別關(guān)注:
- 一個(gè)是QThread的finished信號(hào)對(duì)接QObject的deleteLater使得線程結(jié)束后,繼承QObject的那個(gè)多線程類會(huì)自己銷(xiāo)毀
- 另一個(gè)是QThread的finished信號(hào)對(duì)接QThread自己的deleteLater,這個(gè)不是必須,下面官方例子就沒(méi)這樣做:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};使用QObject創(chuàng)建多線程的方法如下:
- 寫(xiě)一個(gè)繼承QObject的類,對(duì)需要進(jìn)行復(fù)雜耗時(shí)邏輯的入口函數(shù)聲明為槽函數(shù)
- 此類在舊線程new出來(lái),不能給它設(shè)置任何父對(duì)象
- 同時(shí)聲明一個(gè)QThread對(duì)象,在官方例子里,QThread并沒(méi)有new出來(lái),這樣在析構(gòu)時(shí)就需要調(diào)用- – QThread::wait(),如果是堆分配的話, 可以通過(guò)deleteLater來(lái)讓線程自殺
- 把obj通過(guò)moveToThread方法轉(zhuǎn)移到新線程中,此時(shí)object已經(jīng)是在線程中了
- 把線程的finished信號(hào)和object的deleteLater槽連接,這個(gè)信號(hào)槽必須連接,否則會(huì)內(nèi)存泄漏
- 正常連接其他信號(hào)和槽(在連接信號(hào)槽之前調(diào)用moveToThread,不需要處理connect的第五個(gè)參數(shù),否則就顯示聲明用Qt::QueuedConnection來(lái)連接)
- 初始化完后調(diào)用’QThread::start()’來(lái)啟動(dòng)線程
- 在邏輯結(jié)束后,調(diào)用QThread::quit退出線程的事件循環(huán)
到此這篇關(guān)于Qt線程QThread開(kāi)啟和安全退出的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Qt QThread開(kāi)啟和退出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++編程中表達(dá)式的語(yǔ)義與計(jì)算順序
這篇文章主要介紹了C++編程中表達(dá)式的語(yǔ)義與計(jì)算順序,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-01-01
C/C++實(shí)現(xiàn)監(jiān)控目錄文件變化
ReadDirectoryChangesW是Windows操作系統(tǒng)提供的一個(gè)函數(shù),用于監(jiān)視目錄的變化,本文主要為大家介紹了如何使用ReadDirectoryChangesW實(shí)現(xiàn)監(jiān)控目錄文件變化,需要的可以參考下2023-11-11

