2013-03-26 49 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.";