Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Properly terminate a QThread

Tags:

c++

qt

qthread

I've have a worker class that do image acquisition in the background.

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 () are slots and workFinished is a signal.

So in my UI Class, I launch the worker and I connect the signals to the slots :

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();

At this point, I implemented the 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
}

Unfortanely, m_thread->wait() is blocking. Even if the signal quit() is emmited

Thanks

edit:

I added these two connections :

connect(m_worker, SIGNAL(workFinished()), m_worker, SLOT(deleteLater()));
connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater()));

and a Qdebug into acq::~acq()

The message is printed that prove, that stop is called, workFinished is emitted, deleteLater() is emitted.

like image 905
Epitouille Avatar asked Mar 14 '26 14:03

Epitouille


2 Answers

A normal signal/slot connection between objects on different threads requires that the thread of the receiver object runs an event loop.

Your receiver thread does theoretically run its event loop, but the event loop is busy executing the start() slot because run() never returns.

You either need to unblock the receiver event loop or call the stop slot with a Qt::DirectConnection.

When doing the latter you need to be aware that the slot is now called in the context of the sender thread and you need to protect m_started against concurrent access.

Alternatively to using your own flag you could use QThread::requestInterruption() and QThread::isInterruptionRequested()

like image 70
Kevin Krammer Avatar answered Mar 16 '26 03:03

Kevin Krammer


Add

QCoreApplication::processEvents();

to your loop and it'll work.

The reason you deadlock is that the call to acq::run() blocks and does not leave time for acq::stop() to be executed on the worker thread.

like image 26
Ralph Tandetzky Avatar answered Mar 16 '26 03:03

Ralph Tandetzky



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!