QT中的多線程操作有兩種方法可以實現,一種是直接使用官方的QThread,通過重寫QThread::run()方法實現線程處理;另外一種則是通過繼承QObject類的對象,將其實現函數movetoThread()實現。
QThread類中的幾個官方函數和信號的作用
QThread::exec() —— 使線程進入事件循環狀態,並處於wait()狀態,直到調用exit()或quit()退出,一般在run()中調用
QThread::exit() —— 告知線程從事件循環中退出,並返回returnCode的值
QThread::run() —— 線程的運行起點,在調用Start()後調用,從該函數返回將結束線程
QThread::quit() —— 告知線程的事件循環退出並返回0,如果沒有事件循環,這個函數則什麼也不做
QThread::start() —— 調用該函數啓動線程,操作系統會根據優先級來調度
QThread::terminate() —— 終止當前線程,但不發出finish信號,且線程結束後自己不能去做清理工作,較危險
QThread::finished —— finish信號,在線程執行完畢前發出該信號,quit()和wait()調用後線程結束則發出該信號
兩種實現方法有什麼不同?
1、通過QThread::run實現的多線程操作,僅僅run()方法處於新的線程中,而對象的其他非run()函數則仍處於舊線程(即創建QThread對象的線程)中;而繼承於QObject類的實現中,整個新建的線程類都處於新的線程中
2、通過QThread::run實現的多線程如果要支持事件循環,需要在run()中調用QThread::exec(),否則不能正常發送信號;而繼承於QObject的多線程則可以正常使用信號/槽函數機制實現事件循環
繼承QObject的多線程實現
1、創建一個MyThread類,繼承於QObject,將需要做的耗時處理放到函數MyWork()中
2、在MyThread類中創建一個volatile修飾的私有變量isStop用於控制線程的結束,並聲明一個函數用於修改它的值
3、在主窗體Widget的構造函數中分別創建一個MyThread對象m_MyThread和一個QThread對象subthread,通過m_MyThread->moveToThread(subthread)將MyThread對象移至新的子線程中
4、連接主窗體的開始線程信號和m_MyThread的Mywork處理函數;連接子線程的finish()信號和m_MyThread的QObject::deletelater函數
需要注意的關鍵點
1、繼承QObject類的多線程實現中,調用subthread->start()時僅僅是啓動了線程但並沒有進入線程,需要發送開始線程信號才能進入子線程
void Widget::on_Start_Btn_clicked()
{
if(subthread->isRunning())
{
subthread->wait();
return;
}
m_MyThread->setFlag(false);
qDebug() << "set flag = false !";
subthread->start();
//啓動了線程但並沒有進入線程,需要通過信號/槽的方式進入子線程
//直接調用m_MyThread->MyWork()是錯誤的,這樣Mywork是在主線程中處理
emit StartThread();
qDebug() << "發射開始線程信號";
}
2、線程的正確退出步驟應該是:先設置isStop變量。使得線程結束Mywork()中的循環;再調用quit()結束事件循環;最後調用wait()釋放線程槽函數資源退出線程。在這一過程中會發送finish信號,之後會自動調用deletelater刪除線程對象
void Widget::on_close_Btn_clicked()
{
if(subthread->isRunning())
{
qDebug() << "close click!";
m_MyThread->setFlag(true);
qDebug() << "set flag = TRUE";
subthread->quit();
qDebug() << "quit";
subthread->wait();
// if(subthread->isFinished())
// delete subthread;
}
}