Qt在另一个线程中为worker对象排队退出事件

时间:2016-07-06 11:55:08

标签: c++ multithreading qt

MyClass对象有一个工作者QObject,它在另一个线程中工作:

class MyClass {
....
private:
   QThread thread;
   Worker worker; // inherits QObject
};

...

worker.moveToThread(&thread);

现在,当我呼叫thread.exit()时,我的工作线程立即停止。我希望它能完成所有未决事件,然后退出。

我尝试过“排队退出”这样的事情:

connect(this, SIGNAL(signalFinish()),
        &thread, SLOT(quit()),Qt::QueuedConnection);

 ...

void MyClass::finish()
{
    emit signalFinish();
    worker.disconnect(); // do not queue any more events
    thread.wait();
}

但它不起作用。它将永远等待......

如何在事件循环后停止QThread处理所有待处理事件?

2 个答案:

答案 0 :(得分:1)

线程和MyClass实例都存在于同一个线程中 - 可能是主线程:

MyClass::MyClass() {
  ...
  Q_ASSERT(this->thread() == thread.thread());

然而,您排队到worker对象的事件将转到工作线程:

  worker.moveToThread(&thread);
  Q_ASSERT(worker.thread() == &thread);
  Q_ASSERT(worker.thread() != thread.thread());

排队连接完全是错误的事情,因为只要等待工作线程,主线程的事件循环就不会运行,也不会再向线程发出任何事件。 quit调用将永远不会被传递,因为它是在主线程中传递的,但主线程被阻止。

相反,利用quit是一种线程安全的方法。从工作线程本身调用它:

//                                    vvvvvvv thread context object for the call
connect(this, &MyClass::signalFinish, &worker, [this]{ thread.quit(); });
// the `quit()` will execute in `worker.thread()`

请注意,线程上下文对象是将在其中执行thread()调用的对象。在大多数情况下,应该是QThread实例!

当发出signalFinish时,带有上面仿函数的QMetaCallEvent将在工作线程的队列中排队,并在首先处理任何现有的调用(和其他)事件后执行。< / p>

由于你在主线程中调用finish,你可能不想在线程上等待,因为这会阻止GUI并使你的应用程序无响应。

由于您似乎正在向工作线程提交作业项,因此{}可能会更好地为您提供将作业项提交到线程池的QtConcurrent::run,并为您完成所有线程管理。有关完整示例,请参阅this answer。有关相关的花絮,请参阅this answerthat answer

答案 1 :(得分:0)

请参阅this relevant StackOverflow answer.

  

您可以使用QThread::exec()调用在事件循环中运行您的线程。线程将运行它,直到您通过调用QThread::exit()告诉您的线程退出。所以一些示例代码可能如下所示:

void DownloadWorker::run()
 {
    DownloadManager* pDownloadManager = new DownloadManager(this);
    connect(pDownloadManager, SIGNAL(finished()), SLOT(exit()));
    connect(pDownloadManager, SIGNAL(error()), SLOT(exit()));
    pDownloadManager->download();
    exec();
 }
  

这可以保证你的线程不会退出,直到&#34;完成()&#34;发出DownloadManager的信号。

     

注意:这里我举了一个如何解决问题的例子,但我不知道你的整个应用代码。这意味着无法保证此代码是线程安全且一致的。您需要自己处理互斥锁和所有正确的同步。要非常小心 !使用这样一个低水平的&#34;线程API需要很好地理解多线程。