停止Qt线程:调用exit()或quit()不会停止线程执行

Man*_*dar 3 c++ qt qthread

在main()即主线程中创建了一个QThread.将一个worker类移动到新线程.该线程执行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";
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

现在,在'StartThread'完成并正常退出线程之前,我想从主线程中停止线程.用过的,

  1. 主线程中的thread-> exit()和waited(thread-> wait()).
  2. 主线程中的thread-> quit()和waited(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
Run Code Online (Sandbox Code Playgroud)

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

Rei*_*ica 6

isInterruptionRequested只有你打过电话才会返回true QThread::requestInterruption,而不是 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();
};
Run Code Online (Sandbox Code Playgroud)

要运行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();
}
Run Code Online (Sandbox Code Playgroud)

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

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();
}
Run Code Online (Sandbox Code Playgroud)

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

  // *
  QThread::sleep(5);
  worker.stop();
  pool.waitForDone();
}
Run Code Online (Sandbox Code Playgroud)

这个答案有一个将多个作业流式传输到线程池的完整示例.


Mik*_*ike 5

你似乎有很多误解。

从 Qt 文档中,QThread::quit()QThread::exit()

告诉线程的事件循环退出

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

此外,您似乎以QThread::requestInterruption()错误的方式使用,回到Qt 文档

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

所以这并没有真正做任何事情与你的线程,它只是导致后续调用QThread::isInterruptionRequested()返回true,这样当你检测(内螺纹),你应该进行清理,为了尽快(再次退出了quit()对在这里工作,事件循环应该正在运行,所以你应该从你的StartThread这里函数,让线程再次回到它的事件循环中)。

现在回答你的问题:

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

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

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

PS,您不必存储指向您QThread内部的指针Worker即可使用isInterruptionRequested(). 你可以使用QThread::currentThread()->isInterruptionRequested()而不是m_pCurrentThread->isInterruptionRequested()(同样的事情适用于你的退出调用m_pCurrentThread->isInterruptionRequested()