簡體   English   中英

正確終止QThread

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM