C++ 正确终止QThread
我有一个工人班在后台做图像采集C++ 正确终止QThread,c++,qt,qthread,C++,Qt,Qthread,我有一个工人班在后台做图像采集 void acq::run () { while (m_started) { blocking_call(); } emit workFinished(); } void acq::start () { m_started = true; run(); } void acq::stop () { m_started = false; } start()stop()是插槽,workFini
void acq::run ()
{
while (m_started)
{
blocking_call();
}
emit workFinished();
}
void acq::start ()
{
m_started = true;
run();
}
void acq::stop ()
{
m_started = false;
}
start()
stop()
是插槽,workFinished
是信号
因此,在我的UI类中,我启动worker并将信号连接到插槽:
m_thread = new QThread;
m_worker = new acq();
m_worker->moveToThread(m_thread);
// When the thread starts, then start the acquisition.
connect(m_thread, SIGNAL (started ()), m_worker, SLOT (start ()));
// When the worker has finished, then close the thread
connect(m_worker, SIGNAL(workFinished()), m_thread, SLOT(quit()));
m_thread->start();
此时,我实现了slot,closeEvent
void UIClass::closeEvent (QCloseEvent *event)
{
m_worker->stop(); // tell the worker to close
m_thread->wait(); // wait until the m_thread.quit() function is called
event->accept(); // quit the window
}
不幸的是,m_-thread->wait()
正在阻塞。即使发出信号quit()
谢谢
编辑:
我添加了这两个连接:
connect(m_worker, SIGNAL(workFinished()), m_worker, SLOT(deleteLater()));
connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater()));
并将Qdebug插入acq::~acq()
打印的消息证明调用了stop,发出了workFinished,发出了deleteLater()。不同线程上的对象之间的正常信号/插槽连接要求接收方对象的线程运行事件循环 接收器线程理论上确实运行其事件循环,但事件循环正忙于执行
start()
插槽,因为run()
从不返回
您需要解锁接收器事件循环,或者使用Qt::DirectConnection
调用停止槽
在执行后一种操作时,您需要知道插槽现在在发送方线程的上下文中被调用,并且您需要防止并发访问m_start
除了使用自己的标志,您还可以使用QThread::requestInterruption()
和QThread::isInterruptionRequested()
添加
QCoreApplication::processEvents();
到你的循环,它会工作
死锁的原因是对
acq::run()
的调用阻塞了工作线程,没有留出时间在工作线程上执行acq::stop()
在拉尔夫·坦德茨基和凯文·克拉默的帮助下,我终于找到了解决办法
- 而不是用
m_worker->stop()关闭线程代码>,我使用
QMetaObject::invokeMethod(m_worker,“stop”,Qt::ConnectionType::QueuedConnection)代码>和
QCoreApplication::processEvents()工作事件循环中的代码>。这种行为不会改变,但我希望它能防止种族状况或其他问题
- 而不是使用:
connect(m_-worker,SIGNAL(workFinished()),m_-thread,SLOT(quit())代码>,我使用自定义插槽:
我们使用DirectConnection是因为我们在无限循环之外,所以事件不会被处理connect(m_worker, &Acq::workFinished, [=] { std::this_thread::sleep_for(std::chrono::milliseconds(100)); QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection); });
- 说到这里,我遇到了最后一个问题
被阻塞,我必须读取事件,否则,我的自定义槽将永远不会被调用。因此,在我的UI类中添加了一个事件循环m_thread->wait
QEventLoop m\u循环
就在m_-thread->wait()之前,我编写了
m_-loop.exec()代码> 最后,在我的自定义槽中,我放入
m\u loop.quit()
处理事件,直到调用quitm\u loop.exec()
。使用该方法,我甚至不需要m\u loop.quit()
,因为发出m_-thread->wait()
时会调用workFinished
。我不需要m_-loop.quit()
QMetaObject::invokeMethod(m_线程,“退出”,Qt::ConnectionType::DirectConnection)代码>不再
编辑:这个解决方案非常繁重和难看,Qt()建议在我的例子中使用subclass和requestinterruption。您是否使用了调试器来确保等待的线程实际上已经终止了?如果没有终止,它在做什么?感谢您的响应。我有几个问题:-不同线程上的对象之间的正常信号/插槽连接需要接收方对象的线程运行事件循环。接受者是工人吗但是事件循环正忙于执行start()插槽,因为run()永远不会返回。如果工作进程正忙,为什么会调用stop()函数中的qDebug?你有什么例子吗?@submatuille,在你的情况下,
stop()
slot被调用了吗?@Mike是的is@Epitouille,在您将QCoreApplication::processEvents()
添加到while循环后,是否会发生这种情况?否,它从响应的开始阶段开始发生。我不认为QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);这就是解决办法。在acq::run()循环中添加此行会阻塞我的线程。工人被困在这条线上。但是,调用acq::stop()是因为此函数(插槽)中出现了我的qDebug()。@t您是否也QObject::invoke
acq::stop()slot?是的,我将m_worker->stop()更改为QMetaObject::invokeMethod(m_worker,“stop”,Qt::ConnectionType::DirectConnection);它应该是一个Qt::ConnectionType::QueuedConnection
,因此工作线程实际执行它。这避免了m_start
上的数据竞争和这种未定义的行为。另外,我刚刚编辑了我的答案:我从processEvents()
中删除了QEventLoop::WaitForMoreEvents
参数。希望它现在起作用。
connect(m_worker, &Acq::workFinished, [=]
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection);
m_loop.quit();
});