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

Stop a QThread befor quitting application



  • Hi,

    I have toubles stopping a QThread when i quit the application.

    I designed a class ProgramParser that will parse very long text files.

    I have another class ThreadSafeParser that is my interface to use a ProgramParser object in a dedicated QThread through Signal/Slots so my GUI is not blocked during parsers work

    // ThreadSafeParser ctor
       parserThread = new QThread(this);
       parser = new ProgramParser();
       parser->moveToThread(parserThread);
    

    So far, OK. Then I put an instance of ThreadSafeParser as contextProperty to use it from QML.
    And connect QGuiApplication aboutToQuit signal to ThreadsafeParser s safeQuit slot.
    if not, parser will still work even if i quit the app.

    ThreadsafeParser parser;
    engine.rootContext()->setContextProperty("parser",&parser);
    QObject::connect(qApp,&QGuiApplication::aboutToQuit,&parser,&ThreadsafeParser::safeQuit);
    

    this is the content of my safeQuit method

        parserThread->quit();
        parserThread->wait();
    

    and this is my ThreadsafeParser destructor

        ~ThreadsafeParser(){
            parserThread->quit();
            parserThread->wait();
        }
    

    with this method the thread is now stopped but applications will quit with code 1073741845

    I also tryed to call deleteLater instead of my own safeQuit method, in this case work in progress will not even stop.

    Please tell me what i'm doing wrong.



  • WORKING VERSION

    class ProgramParser : public QObject
    {
        Q_OBJECT
    public:
        /// Ctor
        explicit ProgramParser(QObject *parent = nullptr);
    public slots:
        void openFile(QString filePath,int ind);
        void startParsing();// long task
    };
    
    class ThreadsafeParser : public QObject
    {
        Q_OBJECT
        QThread *parserThread;
        ProgramParser *parser;
    public:
        explicit ThreadsafeParser(QObject *parent = nullptr);
    
        ~ThreadsafeParser(){
            qDebug()<<"QUITTING THE QTHREAD";
           // parserThread->quit();  // not needed 
            parserThread->deleteLater();
        }
    signals:
    void s_parse();
    public slots:
        /// Demande de commancer à parser les fichiers
        void startParsing(){
            emit s_parse();
        }
    
    };
    
    ThreadsafeParser::ThreadsafeParser(QObject *parent) : QObject(parent)
    {
        parserThread = new QThread();// QThread(this);
        parser = new ProgramParser;
        parser->moveToThread(parserThread); 
    
      // QObject::connect(parserThread, &QThread::finished, parser, &QObject::deleteLater);
        ///  forward  SIGNALs
        QObject::connect(this,&ThreadsafeParser::s_parse,parser,&ProgramParser::startParsing);
     
        parserThread->start();
    }
    
    

    //main.cpp

    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
       
        QQmlApplicationEngine engine;
      
        ThreadsafeParser safeParser;
         // no neeed to handle this
         //QObject::connect(qApp,&QGuiApplication::aboutToQuit,&safeParser,&ThreadsafeParser::deleteLater);
    
        engine.rootContext()->setContextProperty("parser",&safeParser);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    
    

    Output in qt creator when i quit the app :

    Y :  -178.062 // parse result
    X :  65.4623 // parse result
    Y :  -178.09  // parse result
    QUITTING THE QTHREAD  // destructor called... 
    10:54:45: C:/.../JobPlan exited with code 0
    

  • Moderators

    @LeLev hi

    Inside your save quit, set the pointer to a Nullptr and inside your destructor check against a Nullptr before attempting to quit and wait
    I think, that should help


  • Lifetime Qt Champion

    Hi,

    Out of curiosity, why do that from two different places ?

    Unless something goes wrong, the destructor will be called anyway and again unless something wrong, aboutToQuit also, therefore it's a bit redundant.



  • @SGaist said in Stop a QThread befor quitting application:

    why do that from two different places

    i do from one or the other
    i first tryed to call only deleteLater

    QObject::connect(qApp,&QGuiApplication::aboutToQuit,&parser,&ThreadsafeParser::deleteLater );
    

    and expected this to be called

    ~ThreadsafeParser(){
           parserThread->quit();
           parserThread->wait();
       }
    

    but application is exiting with code 1073741845

    then i have created my safeQuit method

    QObject::connect(qApp,&QGuiApplication::aboutToQuit,&parser,&ThreadsafeParser::safeQuit );
    
    ...
    void safeQuit(){
            parserThread->quit();
            parserThread->wait();
        }
    

    in this case parser is still parsing if i quit app.

    tomorrow i will try to set parserThread = nullptr as @J-Hilk suggested

    ~ThreadsafeParser(){
           parserThread->quit();
           parserThread->wait();
       
          parserThread = nullptr
       }
    

    thx for the interest


  • Lifetime Qt Champion

    How is that ThreadsafeParser object handled ?

    Because I just realised that you are potentially creating a double delete with your deleteLater call since it seems that parser is an object that's on the stack.



  • @SGaist said in Stop a QThread befor quitting application:

    How is that ThreadsafeParser object handled ?

    i create one in my main cpp thats all

    ThreadsafeParser parser;
    engine.rootContext()->setContextProperty("parser",&parser);
    QObject::connect(qApp,&QGuiApplication::aboutToQuit,&parser,&ThreadsafeParser::safeQuit);
    


  • @SGaist said in Stop a QThread befor quitting application:

    parser is an object that's on the stack

    no, it is on heap, in my first post

    parserThread = new QThread(this);
       parser = new ProgramParser();
       parser->moveToThread(parserThread);
    

  • Lifetime Qt Champion

    Then, no it's on the stack as I was talking about your ThreadsafeParser object which is also called parser (this is way using clear variable names is very important).



  • This post is deleted!


  • Hi,
    I came back to the official example (i read it long time ago and was able to design multiple multithreaded application since. Now for some reason i'm stuck ): https://doc.qt.io/qt-5/qthread.html#details

    I do exactly the same thing, My ProgramParser is the Worker of the example.
    And my ThreadsafeParser is the Controller.

    then i my main i just connect app's quit signal to deleteLater slot of My 'Controller' aka ThreadsafeParser

    QObject::connect(qApp,&QGuiApplication::aboutToQuit,&safeParser,&ThreadsafeParser::deleteLater);
    

    so this must be called right ?

    ~ThreadsafeParser(){
        qDebug()<<"QUITTING THE QTHREAD";
        parserThread.quit();
        parserThread.wait();
    }
    

    and since my parserThread 's finished is connected to my ProgramParser (Worker) deleteLater

    QObject::connect(&parserThread, &QThread::finished, parser, &QObject::deleteLater);
    

    I expect this to quit cleanly, but when i quit , the work is still in progress

    edit

    i changed the destructor to

    ~ThreadsafeParser(){
             parserThread->deleteLater();
        }
    

    and putted parserThread on heap instead of stack
    and passed ProgramParser *parser; to member instead of creating it in ThreadsafeParser's ctor

    now QThread is stopped (it looks like) but application don't quit cleanly, i got : app crashed message.

    Can this be related to the compiler i use, i did the exact same thing more than 10 times and it worked perfectly with MinGw compilers, for this project i have to use MSVC17 32bit
    This is the only difference i see



  • WORKING VERSION

    class ProgramParser : public QObject
    {
        Q_OBJECT
    public:
        /// Ctor
        explicit ProgramParser(QObject *parent = nullptr);
    public slots:
        void openFile(QString filePath,int ind);
        void startParsing();// long task
    };
    
    class ThreadsafeParser : public QObject
    {
        Q_OBJECT
        QThread *parserThread;
        ProgramParser *parser;
    public:
        explicit ThreadsafeParser(QObject *parent = nullptr);
    
        ~ThreadsafeParser(){
            qDebug()<<"QUITTING THE QTHREAD";
           // parserThread->quit();  // not needed 
            parserThread->deleteLater();
        }
    signals:
    void s_parse();
    public slots:
        /// Demande de commancer à parser les fichiers
        void startParsing(){
            emit s_parse();
        }
    
    };
    
    ThreadsafeParser::ThreadsafeParser(QObject *parent) : QObject(parent)
    {
        parserThread = new QThread();// QThread(this);
        parser = new ProgramParser;
        parser->moveToThread(parserThread); 
    
      // QObject::connect(parserThread, &QThread::finished, parser, &QObject::deleteLater);
        ///  forward  SIGNALs
        QObject::connect(this,&ThreadsafeParser::s_parse,parser,&ProgramParser::startParsing);
     
        parserThread->start();
    }
    
    

    //main.cpp

    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
       
        QQmlApplicationEngine engine;
      
        ThreadsafeParser safeParser;
         // no neeed to handle this
         //QObject::connect(qApp,&QGuiApplication::aboutToQuit,&safeParser,&ThreadsafeParser::deleteLater);
    
        engine.rootContext()->setContextProperty("parser",&safeParser);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    
    

    Output in qt creator when i quit the app :

    Y :  -178.062 // parse result
    X :  65.4623 // parse result
    Y :  -178.09  // parse result
    QUITTING THE QTHREAD  // destructor called... 
    10:54:45: C:/.../JobPlan exited with code 0
    


  • ... I only removed :

    • my own deletion handling (QObject::connect(qApp,&QGuiApplication::aboutToQuit,&safeParser,&ThreadsafeParser::deleteLater))
    • the parserThread->quit() line in my destructor
    • and the this i was passing to parserThread : (parserThread = new QThread(this);)
      now this works perfectly. Exit with code 0.
     //QObject::connect(qApp,&QGuiApplication::aboutToQuit,&safeParser,&ThreadsafeParser::deleteLater); // no need
     // parserThread->quit();  //no need this in the destructor 
    

    I will update the la code i pasted.

    @J-Hilk and @SGaist thank you


Log in to reply