Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QThread does not quit, why?



  • Hi,

    I am using a QThread and it works fine besides that it does not quit.

        QThread myThread;
        QObject::connect(&app, &QApplication::aboutToQuit, myCtrl, &Control::deleteLater);
        QObject::connect(myCtrl, &Control::finished,  &myThread, &QThread::quit);
        QObject::connect(&myThread, &QThread::finished,  &myThread, &QThread::deleteLater);
        myCtrl->moveToThread(&myThread);
        myThread.start();
    
        //Debug:
        QSignalSpy spy(myCtrl, &Control::finished);  //Count was checked later and signal was emitted correct.
    

    The quit process I start with the "QApplication::aboutToQuit()"-signal. This calls the destructor of my class which looks like this:

    Control::~Control()
    {
        qDebug() << "ControlDTOR called!";
        emit finished();
    }
    

    I see that the destructor was called and also the finished signal was emitted.
    However, "myThread.wait()" never returns.

    What could be the reason for this and how can I make it quit in a proper way?

    Thanks!


  • Moderators

    Please show us the whole piece of code. You're probably blocking the event loop ... edit: or your thread just goes out of scope ...



  • Thanks,

    I now made a MWE:

    main.cpp:

    #include <QApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include <QThread>
    #include <control.h>
    
    //Debug:
    #include <QDebug>
    
    int main(int argc, char *argv[])
    {
        QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QApplication app(argc, argv);
    
        QApplication::setOrganizationName("a");
        QApplication::setOrganizationDomain("b");
        QApplication::setApplicationName("c");
    
        Control* myControl = new Control();
    
        QThread myThread;
        QObject::connect(&app, &QApplication::aboutToQuit, myControl, &Control::deleteLater);
        QObject::connect(myControl, &Control::finished,  &myThread, &QThread::quit);
        QObject::connect(&myThread, &QThread::finished,  &myThread, &QThread::deleteLater);
        myControl->moveToThread(&myThread);
        myThread.start();
    
        QQmlApplicationEngine engine;
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
    
        int exitCode = app.exec();
    
        qDebug() << "Started Waiting:";
        myThread.wait(); // Wait for thread to quit
    
        qDebug() << "Quit"; // This is never reached as myThread.wait() blocks forever.
    
    
        return exitCode;
    }
    

    control.h:

    #ifndef CONTROL_H
    #define CONTROL_H
    
    #include <QObject>
    #include <QDebug>
    
    class Control : public QObject
    {
        Q_OBJECT
    public:
        explicit Control(QObject *parent = nullptr);
        ~Control();
    
    signals:
         void finished();
    
    };
    
    #endif // CONTROL_H
    

    control.cpp:

    #include "control.h"
    
    Control::Control(QObject *parent) : QObject(parent)
    {
    
    }
    
    Control::~Control()
    {
        qDebug() << "Control DTOR called!";
        emit finished();
    }
    
    

    Output after closing the QML window:

    Started Waiting:
    Control DTOR called!
    

  • Lifetime Qt Champion

    @robro said in QThread does not quit, why?:

    QObject::connect(&myThread, &QThread::finished, &myThread, &QThread::deleteLater);

    This will crash since your thread is on the stack.



  • Hi,

    I understand.
    I tried the following, but the thread does still not end:

        QThread *myThread = new QThread();
        QObject::connect(myControl, &Control::finished,  myThread, &QThread::quit);
        QObject::connect(myThread, &QThread::finished,  myThread, &QThread::deleteLater);
    

    What is the problem?



  • @robro The solution is

    QObject::connect(myControl, &Control::finished,  myThread, &QThread::quit, Qt::DirectConnection);
    

    The reason is, when the Control::finished signal is emitted, the myControl object lives in the new thread, but the myThread object lives in the main thread, even though it is a thread object.
    (EDITED)
    So when you use the default connection type (Qt::AutoConnection), and the receiver does not live in the thread that emits the signal, Qt::QueuedConnection will be used.
    That means(from the doc):

    The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.

    But when your signal is emitted, the application is about to quit the main event loop, aka, the event loop of the receiver's thread. So the slot will never be invoked.



  • Thank you very much for the solution and great explanation!


  • Moderators

    @Bonnie said in QThread does not quit, why?:

    The solution is

    
    QObject::connect(myControl, &Control::finished,  myThread, &QThread::quit, Qt::DirectConnection);
    
    

    And where does it say that this slot is thread-safe?

    @robro
    Just quit the thread before you exit the application.

    QObject::connect(&app, &QApplication::aboutToQuit, myCtrl, &Control::deleteLater);
    QObject::connect(myCtrl, &Control::finished,  &myThread, &QThread::quit);
    QObject::connect(&myThread, &QThread::finished, myCtrl, &Control::deleteLater); //< This is needed
    

    Which translates simply to:

    qDebug() << "Started Waiting:";
    myThread.quit();
    myThread.wait(); // Wait for thread to quit
    qDebug() << "Quit";
    

Log in to reply