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
处理所有待处理事件?
答案 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 answer和that 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需要很好地理解多线程。