Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Qt Threading - How to manage a background process

Qt Threading - How to manage a background process

Scheduled Pinned Locked Moved General and Desktop
6 Posts 4 Posters 2.9k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    dhevans79
    wrote on last edited by
    #1

    Hi,

    After spending many, many hours searching around c++ threading, Qt threading etc, I have come here to ask my question...

    I am trying to make a program that has a button that scans the current file system recursively. To prevent hanging the GUI, and to allow the user to cancel the operation once started, I have moved the process to a QObject class which is moved to a thread.

    The slots/signals pattern is used to launch the thread execution, however I am unable to stop the thread. Once the thread has been started, the GUI continuously counts up, but never receives the signal to stop, and does not set _shouldRun to false. Can someone please tell me what I am doing wrong with the code below?

    Thanks in advance for any and all advice.

    backupController.cpp
    @
    #include "backupController.h"
    #include "backupCalculator.h"
    #include <QThread>
    #include <QDebug>

    backupController::backupController() {
    _count = 0;
    }

    backupController::~backupController() {

    }

    bool backupController::startCalculate()
    {

    qDebug("Starting thread");
    
    thread = new QThread;
    backupCalculator* bCalc = new backupCalculator();
    bCalc->moveToThread(thread);
    
    // connect the thread start signals
    connect(thread, SIGNAL(started()), bCalc, SLOT(startCalculate()));
    
    //connect the thread stop signals
    connect(this, SIGNAL(stopCalculating()), bCalc, SLOT(stopCalculate()));
    
    // connect the update SIGNAL from the worker to the controller SIGNAL
    connect(bCalc, SIGNAL(statusUpdate()), this, SLOT(notifyUpdated()));
    
    thread->start();
    
    return true;
    

    }

    bool backupController::stopCalculate() {
    qDebug("Clicked Stop Calculating");
    emit stopCalculating();

    //thread->quit();
    //thread->terminate();
    //thread->exit();
    qDebug("Quitting thread");
    
    return true;
    

    }

    void backupController::notifyUpdated() {
    //qDebug("sending update to master");
    qDebug("count = %d", _count);
    _count++;
    emit notifyUpdate();
    emit countChanged(); // notify QML that the variable has been updated.. yay works!! :)
    }
    @

    backupCalculator.cpp
    @
    #include <QString>
    #include <QDateTime>
    #include <QThread>
    #include <QMutex>
    #include <QWaitCondition>
    #include "backupCalculator.h"

    void backupCalculator::startCalculate(){
    //...
    qDebug("Called StartCalculate");
    _shouldRun = true;

    while (_shouldRun) {
        QMutex dummy;
        dummy.lock();
        QWaitCondition waitCondition;
        qDebug("...sleeping");
        waitCondition.wait(&dummy, 1000);
        //QThread::sleep(1);
        emit statusUpdate();
        qDebug("shouldRun = %d", _shouldRun);
    
    }
    emit statusUpdate();
    

    }

    void backupCalculator::stopCalculate(){
    //...
    qDebug("Called StopCalculate");
    _shouldRun = false;
    qDebug("shouldRun after update = %d", _shouldRun);
    //emit statusUpdate("_shouldRun");
    }
    @

    Which results in the following debug output:
    @
    QML debugging is enabled. Only use this in a safe environment.
    Starting thread
    Called StartCalculate
    ...sleeping
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 0
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 1
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 2
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 3
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 4
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 5
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 6
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 7
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 8
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 9
    Clicked Stop Calculating
    Quitting thread
    Clicked Stop Calculating
    Quitting thread
    shouldRun = 1
    QMutex: destroying locked mutex
    ...sleeping
    count = 10
    @

    1 Reply Last reply
    0
    • JeroentjehomeJ Offline
      JeroentjehomeJ Offline
      Jeroentjehome
      wrote on last edited by
      #2

      Hmm,
      Your general idea is according all the books I read perfect in order. Don't think it has to do with the general idea. It's probably got to do with a "misuse" of a signal/slot somewhere. The output application in QtCreator in debugger mode will give information when a signal/slot is not properly connected. An other idea is to use the "new" Qt5 way of filling out signal/slots:
      @connect(this, &<signalname>, otherclass, &slotname); @
      The big big advantage is better syntax checking so your program is not compilable when the signal/slot could not be connected.
      Also, the connect function returns a bool. If false, the connection could not be made.

      The second thing is that in your backupCalculator::startCalculate() function you define the QWaitCondition to be BlockScope as well as your mutex. Keep in mind that both variables are destroyed every time your while loop has run ones!! Not when your thread is killed. This might cause some problems with your thread synchronization.

      Greetz, Jeroen

      1 Reply Last reply
      0
      • JeroentjehomeJ Offline
        JeroentjehomeJ Offline
        Jeroentjehome
        wrote on last edited by
        #3

        @void backupCalculator::startCalculate(){
        QMutex dummy;
        QWaitCondition waitCondition;
        //...
        qDebug("Called StartCalculate");
        _shouldRun = true;

        while (_shouldRun) {
            dummy.lock();
            qDebug("...sleeping");
            waitCondition.wait(&dummy, 1000);
            //QThread::sleep(1);
            emit statusUpdate();
            qDebug("shouldRun = %d", _shouldRun);
        
        }
        emit statusUpdate();
        

        }@
        This might do the trick. Make them function scope and within the while you're ok to use them.

        Greetz, Jeroen

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Hi,

          One of the main problem is your use of QWaitCondition and QMutex. You never wake your wait condition and currently you cannot because it's scoped to the while loop. The same idea applies to your mutex, it also only exists for the one loop duration, thus you won't protect anything and lock your thread immediately.

          Depending on the operation you want to do, you might want to check QtConcurrent, could be a lot simpler to your use case.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          0
          • D Offline
            D Offline
            dhevans79
            wrote on last edited by
            #5

            So, thanks to you both for your explanations around the Mutex and WaitCondition. This has helped my understanding of how these are used, and has will be very useful as this code is implemented... :)

            However, it didn't obtain the results that I expected, so continued to investigate.

            Finally, I came to the conclusion that whilst the thread was operating on a function, it was not possible to send a second event to the thread for processing, as how would it be able to process the event if it was already running the previous function? (if all that makes sense)

            I changed the boolean to a pointer to the parent class, and removed the second emit. Now the threaded function uses the reference to the boolean, and I can control the execution of the thread from the master thread.

            Here is the solution I have:
            backupController.cpp
            @
            #include "backupController.h"
            #include "backupCalculator.h"
            #include <QThread>
            #include <QDebug>

            #include "counter.h"

            backupController::backupController() {
            _count = 0;
            _executeLoop = false;
            thread = new QThread;
            backupCalculator* bCalc = new backupCalculator();
            bCalc->moveToThread(thread);

            // connect the thread start signals
            connect(this, SIGNAL(startCalculating(bool *)), bCalc, SLOT(startCalculate(bool *)));
            
            //connect the thread stop signals
            connect(this, SIGNAL(stopCalculating()), bCalc, SLOT(stopCalculate()));
            //connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
            
            // connect the update SIGNAL from the worker to the controller SIGNAL
            connect(bCalc, SIGNAL(statusUpdate()), this, SLOT(notifyUpdated()));
            
            thread->start();
            

            }

            backupController::~backupController() {

            }

            void backupController::startCalculate()
            {

            qDebug("Signalling startCalculate()");
            
            _executeLoop = true;
            emit startCalculating(&_executeLoop);
            
            qDebug("Finished Controller Startup");
            

            }

            void backupController::notifyUpdated() {

            qDebug("count = %d",  _count);
            _count++;
            emit notifyUpdate();
            emit countChanged(); // notify QML that the variable has been updated.. yay :)
            

            }

            void backupController::stopCalculate() {
            qDebug("stopping Calculation");

            _executeLoop = false;
            

            }
            @

            backupCalculator.cpp
            @
            #include <QString>
            #include <QDateTime>
            #include <QThread>
            #include <QMutex>
            #include <QWaitCondition>
            #include "backupCalculator.h"

            backupCalculator::backupCalculator(){

            }

            backupCalculator::~backupCalculator(){

            }

            void backupCalculator::startCalculate(bool *_shouldLoop){

            qDebug("Called StartCalculate");
            
            while (*_shouldLoop) {
            
                qDebug("...sleeping");
                QThread::sleep(1);
                
                emit statusUpdate();
                qDebug("shouldRun = %d", *_shouldLoop);
            
            }
            

            }
            @

            1 Reply Last reply
            0
            • I Offline
              I Offline
              ion_knight
              wrote on last edited by
              #6

              Please for next time include the whole of your code as unable to use this as an example because you only detail the .cpp files. Which makes it very hard for anyone to learn from your mistakes.

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved