【问题标题】:Stop Qt Thread : calling exit() or quit() does not stop the thread execution停止 Qt 线程:调用 exit() 或 quit() 不会停止线程执行
【发布时间】:2016-06-24 21:10:16
【问题描述】:

在 main() 中创建了一个 QThread,即主线程。 将工人类移至新线程。线程执行worker类的'StartThread'方法。

工作线程:

//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();
}

现在,在“StartThread”完成并优雅退出线程之前,我想从主线程中停止线程。 用过,

  1. 线程->退出() 并在主线程中等待 (thread->wait())。
  2. 线程->退出() 并在主线程中等待 (thread->wait())。

  3. “StartThread”中的exit()

预期: 一旦从主线程调用 exit()/quit(),线程执行应该停止。

实际: 在所有三个实例中,线程继续运行,完成“StartThread”方法并正常退出。就像没有调用 exit()/quit() 一样好。 输出:

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

是否可以在主线程需要时停止/处置线程?

【问题讨论】:

    标签: c++ qt qthread


    【解决方案1】:

    isInterruptionRequested 仅在您调用 QThread::requestInterruption 时返回 true,不是 QThread::quit。这是记录在案的。

    如果你想使用QThread::quit,线程必须旋转一个事件循环,所以你不应该从它派生;而是这样做:

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

    要运行worker,创建它并将它移动到它的某个线程。要停止工作者,只需在工作者完成之前quit() 其线程即可。

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

    您可能希望改用线程池,而不是手动管理线程的生命周期。

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

    在不运行主事件循环的情况下结束它的另一种方法是:

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

    This answer 有一个将多个作业流式传输到线程池的完整示例。

    【讨论】:

      【解决方案2】:

      你似乎有很多误解。

      来自 Qt 文档,QThread::quit()QThread::exit()

      告诉线程的事件循环退出

      这意味着如果线程的事件循环没有运行(QThread::exec() 没有被调用或者事件循环忙于执行一些繁重的阻塞函数(比如你的StartThread)),线程将不会退出,直到控制又回到了事件循环中。我认为这解释了为什么 quit()exit() 没有按预期为您工作。

      另外你似乎以错误的方式使用QThread::requestInterruption(),回到Qt docs

      请求中断线程。该请求是建议性的,由线程上运行的代码决定是否以及如何根据此类请求采取行动。此函数不会停止线程上运行的任何事件循环,也不会以任何方式终止它。

      所以这实际上对您的线程没有任何作用,它只会导致对QThread::isInterruptionRequested() 的后续调用返回true,因此当您检测到(在线程内)您应该执行清理并退出为尽快(再次为了让quit() 在这里工作,事件循环应该正在运行,所以你应该从这里的StartThread 函数返回,以使线程再次回到它的事件循环中)。

      现在回答你的问题:

      是否可以在主线程需要时停止/处置线程?

      为此,您应该避免运行如此长时间的函数并确保尽快返回事件循环,以便quit()/exit() 可以工作。或者在检测到 isInterruptionRequested() 返回 true 时执行清理并从当前函数返回,以便之后可以调用 quit()/exit()

      正如@kuba 所指出的,您可以选择使用线程池而不是手动管理线程的生命周期。

      附:为了使用isInterruptionRequested(),您不必在Worker 中存储指向QThread 的指针。您可以使用QThread::currentThread()-&gt;isInterruptionRequested() 而不是m_pCurrentThread-&gt;isInterruptionRequested()(同样的事情也适用于您的退出呼叫m_pCurrentThread-&gt;isInterruptionRequested()

      【讨论】: