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

时间:2016-06-24 21:10:17

标签: 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";
}

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-&GT;出口() 并在主线程中等待(thread-&gt; wait())。
  2. thread-&GT;退出() 并在主线程中等待(thread-&gt; wait())。

  3. 'StartThread'中的exit()

  4. 预期:  一旦从主线程调用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
    

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

3 个答案:

答案 0 :(得分:6)

如果您已拨打isInterruptionRequested QThread::requestInterruption,则

QThread::quit仅返回true。这是记录在案的。

如果你想使用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有一个将多个作业流式传输到线程池的完整示例。

答案 1 :(得分: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所指出的,您可以选择使用线程池,而不是手动管理线程的生命。

P.S。您无需在QThread内存储指向Worker的指针,以便使用isInterruptionRequested()。您可以使用QThread::currentThread()->isInterruptionRequested()代替m_pCurrentThread->isInterruptionRequested()(同样适用于您的退出电话m_pCurrentThread->isInterruptionRequested()

答案 2 :(得分:-2)

您在count循环中声明并初始化了while。将其移到外面,它将在每次迭代时停止重新初始化为零,从而按时退出。我不清楚你为什么宣称它static

注意
如果您打算使用排队信号并处理事件,请确保定期在processEvents循环内的线程事件调度程序中调用while

if (thread()->eventDispatcher()->hasPendingEvents()
    thread()->eventDispatcher()->processEvents();