Proper QThread cleanup from inside destructor [SOLVED]
-
I went through the forums many times, but couldn't find a CLEAR solution for my problem.
I have a class encapsulating a QThread and a QObject derived worker. The worker has a timer inside, running every 10ms to poll an external device.
My problem is in the cleanup part: in the destructor I need to stop the timer and destroy the worker first, then the thread.
But if I emit a signal to tell the worker to stop the timer, quit the thread and wait for its completion, the signal will never be emitted because the event loop is
blocked by QThread::wait().
What is the correct way to cleanup in my scenario?class Worker : public QObject { Q_OBJECT public: explicit Worker() : m_pTimer(NULL) {} virtual ~Worker() { if (m_pTimer) delete m_pTimer; } private slots: void init() { m_pTimer = new QTimer(); connect(m_pTimer, SIGNAL(timeout()), this, SLOT(onTimeout())); m_pTimer->start(10); } void onTimeout() { //a device is polled here with an external library } void finish() { m_pTimer->stop(); emit finished(); } private: QTimer* m_pTimer; signals: void finished(); }; class ThreadContainer : public ThreadContainer { Q_OBJECT public: explicit ThreadContainer(QObject* parent = NULL) { worker = new Worker; t = new QThread(this); worker->moveToThread(t); connect(t, SIGNAL(started()), worker, SLOT(init())); connect(this, SIGNAL(sig_finish()), worker, SLOT(finish())); connect(worker, SIGNAL(finished()), t, SLOT(quit())); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); t->start(); }; virtual ~ThreadContainer() { emit sig_finish(); //this is actually emitted after this destructor will exit (queued connection)!!!!! t->quit(); t->wait(); }; signals: void sig_finish(); public slots: private: QThread* t; Worker* worker; }; class Consumer : public QObject { Q_OBJECT public: Consumer() { m_pContainer = new ThreadContainer(); } ~Consumer() { delete m_pContainer; }; private slots: private: ThreadContainer* m_pContainer; protected: };
-
Hi @Maxxximo,
There is nothing wrong with your cleanup code. It is somewhat inefficient, but it should work as expected. What behaviour did you observe?
emit sig_finish(); //this is actually emitted after this destructor will exit (queued connection)!!!!! t->quit();
That comment incorrect. An event loop is not required to emit a signal. Your
sig_finish()
signal is emitted beforet->quit()
is called.After the signal is emitted, the system relies on the worker's event loop to run the
finish()
slot. This is in the worker thread, so it is not affected byt->wait()
in the main thread. -
Hi @Maxxximo,
There is nothing wrong with your cleanup code. It is somewhat inefficient, but it should work as expected. What behaviour did you observe?
emit sig_finish(); //this is actually emitted after this destructor will exit (queued connection)!!!!! t->quit();
That comment incorrect. An event loop is not required to emit a signal. Your
sig_finish()
signal is emitted beforet->quit()
is called.After the signal is emitted, the system relies on the worker's event loop to run the
finish()
slot. This is in the worker thread, so it is not affected byt->wait()
in the main thread.@JKSH said:
There is nothing wrong with your cleanup code. It is somewhat inefficient, but it should work as expected. What behaviour did you observe?
With the above code, Worker::finish() slot will never get called.
I found that removing the call to QThread::quit() and letting the "quit" slot get called from the connection to the "finished()" signal, in this case the Worker::finish() slot gets called:emit sig_finish(); //t->quit(); qDebug() <<" ThreadContainer ::~ThreadContainer , waiting for thread end"; t->wait(); qDebug() <<" ThreadContainer ::~ThreadContainer , exiting destructor";
will log this:
ThreadContainer ::~ThreadContainer , waiting for thread end Worker::finish Worker::~Worker
and then the application gets stuck, waiting for the thread to end.
If I pass a timeout value to wait
t->wait(5000);
will log this:
ThreadContainer ::~ThreadContainer , waiting for thread end Worker::finish Worker::~Worker
then, when the 5 secs timeout has expired
ThreadContainer ::~ThreadContainer , exiting destructor The thread 'QThread' (0x2358) has exited with code 0 (0x0).
Apparently the OS thread will exit only after wait(5000) has returned...there must be something else wrong!
Maybe the full approach is wrong....in the examples on the net, usually the worker decides when it's finished and autonomously emits the finished() signal. In my approach, the ThreadContainer class tells the worker it's time to close.
The reason for this is that I need to free a library handle in the Worker's destructor.