Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop Qt Thread : calling exit() or quit() does not stop the thread execution

Tags:

c++

qt

qthread

Created a QThread in main() i.e Main thread. Moved a worker class to the new thread. The thread executes the 'StartThread' method of worker class.

Worker Thread:

//header file
class Worker : public QObject
{
    Q_OBJECT
public:
    Worker(QThread* thread);

public slots:
    void StartThread(); 
    void EndThread();

private:
    QThread* m_pCurrentThread;
};


// source file
#include "QDebug"
#include "QThread"
#include "worker.h"

Worker::Worker(QThread* thread)
{
m_pCurrentThread = thread;
}

void Worker::StartThread()
{
    qDebug() << " StartThread";

    while(true)
    {
        QThread::msleep(1000);
        qDebug() << " thread running";
        static int count = 0;
        count++;
        if(count == 10)
        {            
            break;
        }
        if(m_pCurrentThread->isInterruptionRequested())
        {
            qDebug() << " is interrupt requested";
            // Option 3:  
            m_pCurrentThread->exit();               
        }
    }
    qDebug() << "StartThread finished";
}

void Worker::EndThread()
{
    qDebug() << "thread finished";
}

Main.cpp

#include <QCoreApplication>
#include "worker.h"
#include "QThread"
#include "QObject"
#include "QDebug"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QThread* thread = new QThread();
    Worker* workerObj = new Worker(thread);

    QObject::connect(thread,
                     SIGNAL(started()),
                     workerObj,
                     SLOT(StartThread()));
    QObject::connect(thread,
                     SIGNAL(finished()),
                     workerObj,
                     SLOT(EndThread()));



    workerObj->moveToThread(thread);
    thread->start();
    thread->requestInterruption();
    QThread::msleep(2000);
    qDebug() << "terminate thread";
    if(thread->isRunning())
    {   
        // Option 1,2 exit() / quit() used but got same output
        thread->exit(); 
        qDebug() << "wait on  thread";
        thread->wait();
    }
    qDebug() << " exiting main";

    return a.exec();
}

Now before the 'StartThread' completes and thread is exited gracefully I want to stop the thread from Main thread. Used,

  1. thread->exit() and waited (thread->wait()) in the Main thread.
  2. thread->quit() and waited (thread->wait()) in the Main thread.

  3. exit() in the 'StartThread'

Expected: The thread execution should stop as soon as exit()/quit() is called from Main thread.

Actual: In all three instances the thread keeps running, completes the 'StartThread' method and exits gracefully. As good as not exit()/quit() was called. output:

StartThread
 thread running
 is interrupt requested
terminate thread
wait on  thread
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
 is interrupt requested
 thread running
StartThread finished
thread finished
exiting main

Is it possible to stop/dispose the thread as in when required by the main thread?

like image 880
Mandar Avatar asked Jun 24 '16 21:06

Mandar


People also ask

How do I stop a thread in a qthread?

QThread will notifiy you via a signal when the thread is started () and finished () , or you can use isFinished () and isRunning () to query the state of the thread. You can stop the thread by calling exit () or quit () . In extreme cases, you may want to forcibly terminate () an executing thread. However, doing so is dangerous and discouraged.

What is a qthread in Qt?

A QThread object manages one thread of control within the program. QThreads begin executing in run (). By default, run () starts the event loop by calling exec () and runs a Qt event loop inside the thread. You can use worker objects by moving them to the thread using QObject::moveToThread ().

What happens when qthread is called with termination disabled?

If termination has been deferred (i.e. QThread::terminate () was called with termination disabled), this function will terminate the calling thread immediately. Note that this function will not return in this case.

Does this function stop an event loop on the thread?

This function does not stop any event loop running on the thread and does not terminate it in any way.


2 Answers

isInterruptionRequested only returns true if you've called QThread::requestInterruption, not QThread::quit. This is documented.

If you want to use QThread::quit, the thread must spin an event loop, so you shouldn't be deriving from it; instead do this:

class Worker : public QObject {
  QBasicTimer m_timer;
  int chunksToDo = 20;
  void doChunkOfWork() {
    QThread::sleep(1);
  }
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() != m_timer.timerId()) return;
    if (!chunksToDo) { m_timer.stop(); return; }
    doChunkOfWork();
    if (!--chunksToDo) emit done();
  }
public:
  explicit Worker(QObject * parent = nullptr) : QObject{parent} {
    m_timer.start(0, this);
  }
  Q_SIGNAL void done();
};

To run the worker, create it and move it to some its thread. To stop the worker, just quit() its thread before the worker is done.

int main(int argc, char ** argv) {
  QCoreApplication app{argc, argv};
  Worker worker;
  QThread thread;
  worker.moveToThread(&thread);
  QObject::connect(&worker, &Worker::done, &thread, &QThread::quit);
  QObject::connect(&thread, &QThread::finished, &app, &QCoreApplication::quit);
  thread.start();
  return app.exec();
}

Instead of managing the thread's life manually, you might wish to use a thread pool instead.

class Worker : public QObject, QRunnable {
  int chunksToDo = 20;
  volatile bool active = true;
  void doChunkOfWork() {
    QThread::sleep(1);
  }
  void run() {
    while (active && chunksToDo) {
      doChunkOfWork();
      --chunksToDo;
    }
    emit done();
  }
public:
  using QObject::QObject;
  Q_SLOT void stop() { active = false; }
  Q_SIGNAL void done();
};

int main(int argc, char ** argv) {
  QCoreApplication app{argc, argv};
  QThreadPool pool;
  Worker worker;
  worker.setAutoDelete(false); // important!
  pool.start(&worker);
  // *
  QTimer::singleShot(5000, &worker, &Worker::stop);
  QObject::connect(&worker, &Worker::done, &app, &QCoreApplication::quit);
  return app.exec();
}

An alternate way to end it without running the main event loop would have been:

  // *
  QThread::sleep(5);
  worker.stop();
  pool.waitForDone();
}

This answer has a complete example of streaming multiple jobs to a thread pool.

like image 51
Kuba hasn't forgotten Monica Avatar answered Oct 21 '22 06:10

Kuba hasn't forgotten Monica


You seem to have many misconceptions.

From the Qt docs, both QThread::quit() and QThread::exit():

Tell the thread's event loop to exit

That means that if the thread's event loop is not running (either QThread::exec() have not been called or the event loop busy executing some heavy blocking function(like your StartThread)) , the thread will not quit until the control is back into the event loop. I think that explains why quit() and exit() did not work for you as expected.

Also you seem to be using QThread::requestInterruption() in a wrong way, back to the Qt docs:

Request the interruption of the thread. That request is advisory and it is up to code running on the thread to decide if and how it should act upon such request. This function does not stop any event loop running on the thread and does not terminate it in any way.

so this does not really do any thing with your thread, it just causes subsequent calls to QThread::isInterruptionRequested() to return true, so that when you detect that (within the thread) you should perform clean-up and quit as soon as possible (again in order for quit() to work here, the event loop should be running, so you should return from your StartThread function here, to get the thread back into its event loop again).

Now to answer your question:

Is it possible to stop/dispose the thread as in when required by the main thread?

To do that, you should either avoid such long running functions and make sure you return to the event loop as soon as possible, so that quit()/exit() can work. Or perform clean-up and return from the current function as soon as you detect that isInterruptionRequested() is returning true, so that calls to quit()/exit() can work after that.

As pointed out by @kuba you may choose to use a thread pool instead of managing your thread's life manually.

P.S. you don't have to store a pointer to your QThread inside the Worker in order to use isInterruptionRequested(). you can use QThread::currentThread()->isInterruptionRequested() instead of m_pCurrentThread->isInterruptionRequested() (the same thing applies to your exit call m_pCurrentThread->isInterruptionRequested())

like image 26
Mike Avatar answered Oct 21 '22 05:10

Mike