[英]Properly terminate a 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 ()
是插槽, 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_started
防止並發訪問。
或者使用自己的標志,你可以使用QThread::requestInterruption()
和QThread::isInterruptionRequested()
加
QCoreApplication::processEvents();
到你的循環,它會工作。
你死鎖的原因是對acq::run()
的調用阻塞並且沒有留出時間讓acq::stop()
在工作線程上執行。
在Ralph Tandetzky和Kevin Krammer的幫助下,我終於找到了解決方案。
而不是用m_worker->stop();
關閉線程m_worker->stop();
,我使用QMetaObject::invokeMethod(m_worker, "stop", Qt::ConnectionType::QueuedConnection);
和QCoreApplication::processEvents();
在worker事件循環中。 行為不會改變,但我希望它能防止競爭或其他問題。
而不是使用: connect(m_worker, SIGNAL(workFinished()), m_thread, SLOT(quit()));
,我使用自定義插槽:
connect(m_worker, &Acq::workFinished, [=] { std::this_thread::sleep_for(std::chrono::milliseconds(100)); QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection); });
我們使用DirectConnection,因為我們在無限循環之外,因此不處理事件。
有了這個,我有一個最后的問題。 m_thread->wait
is blocking,我要讀取事件,否則,我的自定義插槽永遠不會被調用。 所以在我的UI類QEventLoop m_loop
添加了一個事件循環。
就在m_thread->wait()
,我寫了m_loop.exec();
最后,在我的自定義插槽中,我放了m_loop.quit()
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(); });
m_loop.exec()
進程事件直到退出m_loop.quit()
被調用。 使用該方法,我甚至不需要m_thread->wait()
因為在發出workFinished
時會調用m_loop.quit()
。 我不需要QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection);
再
現在它就像一個魅力
編輯:這個解決方案非常沉重和丑陋,Qt( https://www.qtdeveloperdays.com/sites/default/files/David%20Johnson%20qthreads.pdf )sugest在我的情況下使用子類和requestInteruption。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.