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

2

5

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?

Occupy answered 24/6, 2016 at 21:10 Comment(0)
P
7

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.

Picoline answered 24/6, 2016 at 23:44 Comment(0)
C
5

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())

Couch answered 25/6, 2016 at 4:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.