2013-03-26 50 views
1

我試圖告訴一個線程優雅地退出。爲此,線程在每次迭代中檢查全局布爾標誌,該標誌指示線程是否應該繼續或退出。線程是類似這樣的設置(代碼爲http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/):爲什麼我的線程不能正常退出?

ImageFusionQt::ImageFusionQt(QWidget* parent) 
: QMainWindow(parent) 
{  

captureThread = new QThread(); 
captureWorker = new CaptureWorker(); 

// Connects the threads started() signal to the process() slot in the worker, causing it to start. 
connect(captureThread, SIGNAL(started()), captureWorker, SLOT(process())); 

// Connect worker finished signal to trigger thread quit, then delete. 
connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit())); 
connect(captureWorker, SIGNAL(finished()), captureWorker, SLOT(deleteLater())); 

// Make sure the thread object is deleted after execution has finished. 
connect(captureThread, SIGNAL(finished()), captureThread, SLOT(deleteLater())); 

// Give QThread ownership of Worker Object 
captureWorker->moveToThread(captureThread); 
captureThread->start(); 

} 

CaptureWorker.cpp

void CaptureWorker::process() 
{ 
    while(true) 
    { 
    g_exit_lock->lockForRead(); 
    if(g_exit) 
    { 
     g_exit_lock->unlock(); 
     break; 
    } 
    g_exit_lock->unlock(); 
    } 
    qDebug() << "CaptureWorker: Exiting."; 
    emit finished(); 
} 

現在,當我試圖通過一些功能的標誌設置爲true停止線程時, process()方法返回,但線程沒有完成,wait()的調用永遠阻塞。爲什麼我的線程不終止?

g_exit_lock->lockForWrite(); 
g_exit = true; 
g_exit_lock->unlock(); 

QThread::sleep(15); 
qDebug() << "ct finished? " << captureThread->isFinished(); 

captureThread->wait(); 
qDebug() << "All threads stopped."; 

日誌文件輸出:

2013.03.26 09:29:22[D] CaptureWorker: Exiting. 
2013.03.26 09:29:37[D] ct finished? false 

UPDATE

我做了一些調試,發現了一些野趣的東西:

  • 在事件循環的線程塊(QEventLoop :: EXEC)。它等待顯然沒有收到的quit()信號。
  • 線程的事件循環在 process()返回後創建。通過像我一樣連接信號,線程的run()方法在線程完成其工作(例如返回的process())後被調用。
  • 事件循環會在執行實際循環之前清除所有已發佈的退出事件。

我的結論

  • 連接,因爲我根本不起作用,因爲退出()當事件循環建立
  • 調用Quit()成員事件被刪除的退出()槽函數直接在線程對象上,顯然會導致優美的終止。這可以通過使用QThread :: currentThread() - > quit();從外部或從內部完成。

開放性問題

  • 有沒有辦法來調用process()方法的事件循環已經建立之後?
  • 感覺不對,事情循環是在工作完成時創建的。但是,我使用QThread的方式與docs
+0

也許你只需要刷新調試流? – Nick 2013-03-26 09:02:46

+0

finished()的內容是什麼? – 2013-03-26 09:23:41

+0

日誌記錄是一種*非常*非生產性的方式來調試程序。學習如何使用調試器。只需連接調試器,中斷程序,將上下文切換到工作線程,並從堆棧跟蹤中找出它正在做什麼。有很高的可能性,你現在會發現它不是執行循環,而是埋在某種操作系統調用中,等待捕獲完成。所以它不會測試g_exit,所以它不會退出。 – 2013-03-26 10:10:36

回答

2

這裏發生的是quit根本不被調用。 你知道Qt有直接或排隊的連接。當你使用這個

connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit())); 

這是一個真正的自動連接類型。因此,因爲您將captureWorker移至另一個線程,所以使用Qt::QueuedConnecton。將在主要線程上調用quit()。但是,您所做的是阻止主線程與captureThread->wait();
quit()排隊在事件循環中,但它被阻止,因爲它正在等待線程完成。
所以,你可能想直接調用退出()一樣,直接通過更換

connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit())); 

connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit()), Qt::DirectConnection); 

或致電退出()就像你在編輯建議,或者你也可以這樣做而不是captureThread->wait();以下,如果你真的需要

QEventLoop l; 
    l.connect(captureThread, SIGNAL(finished()), SLOT(quit())); 
    l.exec(QEventLoop::ExcludeUserInputEvents); 

或者您可以連接到線程的結束()信號和做WH你想在captureThread->wait();之後做的事情,所以你不必手動等待。
在Qt中做事有很多種方式

+0

感謝您的輸入。順便說一句,當你給出這個答案時,我更新了我的問題。我很難理解你的答案:我知道captureThread存在於主線程中,但是調用它的quit()插槽應該退出它正在管理的線程,不是嗎?而且我不明白你的建議比僅僅執行captureThread-> quit()更好。你可以擴展嗎?非常感謝。 – binford 2013-03-26 13:11:29

+0

是的,調用quit()會停止事件循環。問題在於它被調用的地方。由於captureThread存在於主線程中,所以您應該在主線程中調用它的方法,因爲Qt類是可重入的。 – spiritwolfform 2013-03-26 13:25:24

+0

順便說一句,我不認爲線程的事件循環是在process()返回後創建的。也許你已經混淆了主線程的事件循環和工作線程事件循環 – spiritwolfform 2013-03-26 13:31:18

0

問題是你實際上阻塞了主線程和事件循環,所以結果不讓線程接收到退出信號。 這個問題將通過如下步驟修復:

g_exit_lock->lockForWrite(); 
g_exit = true; 
g_exit_lock->unlock(); 

QThread::sleep(15); 
qDebug() << "ct finished? " << captureThread->isFinished(); 

// You are missing below line, without this, main thread will block 
captureThread->quit(); 
captureThread->wait(); 

qDebug() << "All threads stopped.";