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:
    };
    

  • Moderators

    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 before t->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 by t->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.



  • I think I've got it!
    Used a DirectConnection here:

    connect(m_worker, SIGNAL(finished()), m_thread, SLOT(quit()), Qt::DirectConnection);
    

    the quit slot now is getting called and it works like a charm!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.