Qt線程QThread開啟和安全退出的實(shí)現(xiàn)
1、線程開啟
Qt中,開啟子線程,一般有兩種方法:
a, 定義工作類worker:
worker繼承 QThread, 重寫run函數(shù),在主線程中實(shí)例化worker,把耗時(shí)工作放進(jìn)worker的run函數(shù)中完成,結(jié)束后,往主線程中發(fā)信號,傳遞參數(shù)即可。
注意:此worker的實(shí)例,只有run函數(shù)在子線程中執(zhí)行,worker的其他函數(shù),均在主線程中執(zhí)行。
如果子線程已經(jīng)start開啟,run函數(shù)尚未運(yùn)行完時(shí),再次start,此時(shí)子線程不會有任何操作,run函數(shù)不會被重新調(diào)用,會繼續(xù)執(zhí)行run函數(shù)。
b, 定義工作類worker:
worker繼承Qobject,在worker中完成耗時(shí)操作,并在主線程中 #include “worker.h”進(jìn)來,隨后,在主線程中New出幾個(gè)子線程QThread,使用moveToThread()函數(shù),把worker轉(zhuǎn)移進(jìn)入子線程,例如:
pWorker = new Worker; pThread1 = new QThread; pWorker->moveToThread(pThread1)
之后,再根據(jù)需求,看何時(shí)開啟子線程:pThread1->start();
如果線程已經(jīng)運(yùn)行,你重復(fù)調(diào)用start其實(shí)是不會進(jìn)行任何處理。
2、線程關(guān)閉
對于上面a類,在run中開啟的子線程,如果run中沒有調(diào)用exec(),使用quit(),exit(),是無法跳出run中的循環(huán),終止子線程的。不會發(fā)生任何效果,QThread不會因?yàn)槟阏{(diào)用quit()函數(shù)而退出正在運(yùn)行到一半的run。
但使用QThread的terminate()方法,可以立刻結(jié)束子線程,但這個(gè)函數(shù)存在非常不安定因素,不推薦使用。那么如何安全的終止一個(gè)線程呢?
最簡單的方法是添加一個(gè)bool變量,通過主線程修改這個(gè)bool變量來進(jìn)行終止,但這樣有可能引起訪問沖突,需要對其進(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);// 此處加鎖,防止訪問沖突 if(!m_isCanRun)//在每次循環(huán)判斷是否可以運(yùn)行,如果不行就退出循環(huán) { return; } } } }
因此在子線程的run函數(shù)的循環(huán)中遇到m_isCanRun的判斷后就會退出run函數(shù),繼承QThread的函數(shù)在運(yùn)行完run函數(shù)后就視為線程完成,會發(fā)射finish信號。
子線程指針,盡量不要去delete ,這樣不安全。一般會綁定QObject::deleteLater()方法。
connect(pThread,&QThread::finished ,thread,&QObject::deleteLater);
線程結(jié)束后調(diào)用deleteLater來銷毀分配的內(nèi)存。
對于上面b類,在Qt4.8之后,Qt多線程的寫法最好還是通過QObject來實(shí)現(xiàn),和線程的交互通過信號和槽(實(shí)際上其實(shí)是通過事件)聯(lián)系。
繼承QObject多線程的方法線程的創(chuàng)建很簡單,只要讓QThread的start函數(shù)運(yùn)行起來就行,但是需要注意銷毀線程的方法: 在線程創(chuàng)建之后,這個(gè)QObject的銷毀不應(yīng)該在主線程里進(jìn)行,而是通過deleteLater槽進(jìn)行安全的銷毀,因此,繼承QObject多線程的方法在創(chuàng)建時(shí)有幾個(gè)槽函數(shù)需要特別關(guān)注:
- 一個(gè)是QThread的finished信號對接QObject的deleteLater使得線程結(jié)束后,繼承QObject的那個(gè)多線程類會自己銷毀
- 另一個(gè)是QThread的finished信號對接QThread自己的deleteLater,這個(gè)不是必須,下面官方例子就沒這樣做:
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)建多線程的方法如下:
- 寫一個(gè)繼承QObject的類,對需要進(jìn)行復(fù)雜耗時(shí)邏輯的入口函數(shù)聲明為槽函數(shù)
- 此類在舊線程new出來,不能給它設(shè)置任何父對象
- 同時(shí)聲明一個(gè)QThread對象,在官方例子里,QThread并沒有new出來,這樣在析構(gòu)時(shí)就需要調(diào)用- – QThread::wait(),如果是堆分配的話, 可以通過deleteLater來讓線程自殺
- 把obj通過moveToThread方法轉(zhuǎn)移到新線程中,此時(shí)object已經(jīng)是在線程中了
- 把線程的finished信號和object的deleteLater槽連接,這個(gè)信號槽必須連接,否則會內(nèi)存泄漏
- 正常連接其他信號和槽(在連接信號槽之前調(diào)用moveToThread,不需要處理connect的第五個(gè)參數(shù),否則就顯示聲明用Qt::QueuedConnection來連接)
- 初始化完后調(diào)用’QThread::start()’來啟動線程
- 在邏輯結(jié)束后,調(diào)用QThread::quit退出線程的事件循環(huán)
到此這篇關(guān)于Qt線程QThread開啟和安全退出的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Qt QThread開啟和退出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C/C++實(shí)現(xiàn)監(jiān)控目錄文件變化
ReadDirectoryChangesW是Windows操作系統(tǒng)提供的一個(gè)函數(shù),用于監(jiān)視目錄的變化,本文主要為大家介紹了如何使用ReadDirectoryChangesW實(shí)現(xiàn)監(jiān)控目錄文件變化,需要的可以參考下2023-11-11