Deleting object in QThread



  • Hi all,

    I'm using the worker-object approach, and I would like to understand once and for all, what is the proper way to release resources that live in another thread and --what exactly happens there?!--.
    So, this is the sketch of my code's structure (any comments regarding improving the structure are of course wellcommed - I'm using Qt::BlockingQueuedConnection on purpose, because there are multiple worker objects and I want their creation and initialization order to be precise):

    @class Worker : QObject
    {
    Q_OBJECT

    public:
    Worker()

    {/*initialization code:
    init timer and connect its signal to worker */
    }

    ~Worker(){}

    public slots:
    void startWorker()
    {
    if (QThread::currentThread() == this->thread())
    doActualStart();
    else
    QMetaObject::invokeMethod(this, "doActualStart", Qt::BlockingQueuedConnection);
    }

    void stopWorker()
    {
    if (QThread::currentThread() == this->thread())
    doActualClose();
    else
    QMetaObject::invokeMethod(this, "doActualClose", Qt::BlockingQueuedConnection);
    }

    private slots:
    void doActualStart()
    {
    //start timer
    }

    void doActualClose()
    {
    //stop timer
    }

    void timerSlot()
    {
    //do Logic
    }

    private:
    m_Timer;
    };

    class Manager
    {
    /Lives in main thread/

    void createWorker()
    {
    Worker* worker = new Worker();
    QThread* workerThread = new QThread();

    worker->moveToThread(workerThread());
    connect(worker, SIGNAL(destroyed()), workerThread, SLOT(quit()));

    workerThread->start();
    worker->startWorker();
    }

    void deleteWorker(Worker* worker)
    {
    //aquire worker thread
    QThread* workerThread = /non trivial aquisition code, since worker->thread() returns const/;

    worker->stopWorker();
    QMetaObjectInvoke(worker, "deleteLater", Qt::BlockingQueuedConnection);

    workerThread->wait();
    }

    };@

    My questions:

    1. When calling deleteWorker(w), the application hangs on workerThread->wait(); Does this mean that the worker project does not get deleted for some reason? or maybe the problem is somehow related to connection type of destroyed() to quit() connection?

    2. What is the proper way to delete resources after moving them to a QThread object? In worker-object examples,
      I saw the next code:

    @connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));@

    So what exactly happens when worker emits finished()? Is the worker object supposed to be deleted from a specific thread? or it may be deleted from the main thread or the worker thread, depending on when the thread object quits its event loop?

    Thanks in advance



  • Hi,

    1. QObject:deleteLater() schedules a delete operation, it doesn't delete the object, therefore you don't need to call deleteLater() the way you do. worker->deleteLater() would suffice. That behavior of deleteLater() also explains why your blocking call doesn't block the main thread: deleteLater() returns without blocking.

    If the worker thread is idle, I don't see any reason why workerThread->wait() would block for so lang that you notice it. The QObject::destroyed() signal should make the thread exit its event loop.

    1. You should strive to delete a QObject in the thread it lives. This is important when you have QTimers, QTcpSockets etc.

    I would prefer the following chain of messages to delete a QObject instance living in a QThread other than the main thread:
    @
    connect( object, SIGNAL(destroyed), thread, SLOT(quit()) );
    connect( thread, SIGNAL(finished()), thread, SLOT(deleteLater()) );
    @

    And I would do the following in the main thread:
    @
    object->deleteLater();
    thread->wait();
    @

    If you are care about destructing all your objects properly when main() returns, than don't forget to give a parent to QThread objects, because the main event loop may exit before the delete operation scheduled by QThread::deleteLater() is executed.



  • I understood what caused the application to hang - the problem was, as I assumed, with

    @connect(worker, SIGNAL(destroyed()), workerThread, SLOT(quit));@

    Since these two objects live in different threads, Qt::QueuedConnection type is used, which means when worker object is deleted, the event is posted to the main thread's event loop. When the control returns to the main thread, the call to workerThread->wait() is made before the main thread got a chance to process the event that was posted because of worker deletion.

    If I add for example (Not that I promote such approach, its for demonstration purposes only)

    @worker->stopWorker();
    QMetaObjectInvoke(worker,"deleteLater",Qt::BlockingQueuedConnection);
    QCoreApplication::processEvents(QEventLoop::AllEvents, 10000);

    workerThread()->wait();@

    Then the worker thread quits quietly and everything is ok...



  • You are right. Just add workerThread->quit() before workerThread->wait() . And worker->deleteLater() before that.



  • But how can I be sure that the worker thread doesn't quit before processing worker's deleteLater event? Before the thread quits its event loop, does it make sure it processes all deleteLater events?



  • [quote author="bobdark" date="1420553165"]But how can I be sure that the worker thread doesn't quit before processing worker's deleteLater event? Before the thread quits its event loop, does it make sure it processes all deleteLater events?[/quote]

    Yes it does.


  • Moderators

    From the "QThread documentation":http://doc.qt.io/qt-5/qthread.html#managing-threads --

    [quote]From Qt 4.8 onwards, it is possible to deallocate objects that live in a thread that has just ended, by connecting the finished() signal to QObject::deleteLater().[/quote]



  • Awesome, thanks


Log in to reply
 

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