为什么我的线程不能优雅地退出?

时间:2013-03-26 08:56:03

标签: c++ multithreading qt visual-c++ qt5

我尝试告诉线程优雅地退出。为此,线程在每次迭代中检查一个全局布尔标志,该标志指示线程是应该继续还是退出。线程设置如下(代码来自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 

更新

我做了一些调试,发现了一些有趣的东西:

  • 线程在其事件循环中阻塞(QEventLoop :: exec)。它等待一个显然没有收到的quit()信号。
  • 线程的事件循环在 process()返回后创建。通过像我一样连接信号,线程的run()方法在线程完成其工作后被调用(例如,返回了process())。
  • 事件循环在执行实际循环之前清除所有发布的退出事件。

我的结论

  • 像我一样连接quit()插槽不起作用,因为在建立事件循环时删除了quit()事件
  • 直接在线程对象上调用quit()成员函数,显然会导致正常终止。这可以通过使用QThread :: currentThread() - &gt; quit();从外部或从内部完成;

打开问题

  • 有没有办法在建立事件循环后调用process()方法?
  • 在工作完成后创建事件循环感觉不对。但是,我使用QThread的方式与docs
  • 一致

2 个答案:

答案 0 :(得分:2)

这里发生的是quit根本没有被调用。 你知道Qt有直接连接或排队连接。当你使用这个

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

它真的是一种自动连接类型。因此,因为您将captureWorker移动到另一个线程,所以使用了Qt::QueuedConnecton。将在主题上调用quit()。但你所做的是用captureThread->wait();来阻止主线程 quit()在事件循环中排队,但它被阻止,因为它等待线程完成。
所以你可能想直接调用quit(),比如替换

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

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

或者像编辑中建议的那样直接调用quit(),或者如果你真的需要,你也可以执行以下操作而不是captureThread->wait();

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

或者你可以连接到线程的完成()信号并在captureThread->wait();之后做你想做的事情,这样你就不必手动等待了。
在Qt

中有很多方法可以做

答案 1 :(得分: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.";