Qt中当程序结束时线程的退出


在Qt程序结束时应该如何退出正在运行的任务子线程?

因个人经验和能力有限,本文仅供参考,有错误或者考虑不完善的地方请各位批评指正。

一、正常情况下如何创建和退出线程

1.继承QThread,重写run()函数

点击折叠或展开代码
// 类的定义
class WorkThread1 : public QThread
{
public:
    explicit WorkThread1(QObject *parent = 0);
signals:
    // 线程运行完成
    void workDone();
public slots:
    // 停止线程
    void stopThread();
protected:
    // 重写run()函数
    virtual void run() override;
private:
    // 是否停止运行
    bool isStop = false;
};

WorkThread1::WorkThread1(QObject *parent) : QThread(parent)
{

}
void WorkThread1::run()
{
    for (int i = 0; i < 10; ++i) {
        if (isStop) {
            return ;
        }
        // 执行耗时操作
        // TODO
    }
}
void WorkThread1::stopThread()
{
    isStop = true;
}

// 外部调用
// 两个按钮,分别开启和停止线程
QPushButton *start = new QPushButton(this);
QPushButton *stop = new QPushButton(this);
WorkThread1 *thread1 = new WorkThread1(this);
connect(start, &QPushButton::clicked, [=](){
    thread1->start();
});
connect(stop, &QPushButton::clicked, [=](){
    thread1->stopThread();
    thread1->wait();
});

2.Qt官方推荐做法,使用moveToThread函数
点击折叠或展开代码
// 任务类
class Worker : public QObject
 {
     Q_OBJECT
 public slots:
     void doWork(const QString &parameter) {
         QString result;
         // 耗时操作
         // TODO
         emit resultReady(result);
     }

 signals:
     void resultReady(const QString &result);
 };

 // 外部调用
 class Controller : public QObject
 {
     Q_OBJECT
     // 使用智能指针列表
     QList<QPointer<QThread>> workerThreadList;
 public:
     Controller() {
         Worker *worker = new Worker;
   	  QPointer<QThread> workerThread = new QThread;
   	  // 加入管理列表
   	  workerThreadList.append(workerThread);
   	  // 将任务类移入线程
         worker->moveToThread(workerThread);
   	  // 连接信号
         connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
         connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
         connect(this, &Controller::operate, worker, &Worker::doWork);
         connect(worker, &Worker::resultReady, this, &Controller::handleResults);
   	  // 启动线程
         workerThread->start();
     }
     ~Controller() {
         for (QPointer<QThread> thread : qAsConst(workerThreadList)) {
   	      if (! thread.isNull) {
   		      thread.quit();
   		      thread.wait();
   		  }
   	  }
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };

3. 其他方法快速创建线程任务
点击折叠或展开代码
// QThread静态函数create
QThread *thread = QThread::create([=](){
    // 耗时操作
    // TODO
});
// QtConcurrent模块,使用时需要在pro文件中引入QT += concurrent,使用头文件#include <QtConcurrent>,可在帮助文档中搜索 Concurrent Run
QtConcurrent::run([=](){
    // 耗时操作
    // TODO
});
// QRunnable 除非有线程池的需求,否则个人不推荐使用。因为使用起来需要继承和重写,而且使用信号和槽比较麻烦
MyRunnable *r = new MyRunnable;   // 需要继承自QRunnable,重写run函数
QThreadPool threadpool;
threadpool.setMaxThreadCount(1);
threadpool.start(r);

二、程序退出时如何退出正在运行的线程

  1. 在耗时的循环操作中添加标志位的判断,在退出时将标志位设置成退出标志位,比如上面的WorkThread1 示例中重写的run函数中的处理方式,如果层级太多,该用全局变量就使用全局变量
  2. 翻阅帮助文档发现QThread有以下两个函数,和标志位差不多的用法
点击折叠或展开代码
    // 请求中断线程
    void QThread::requestInterruption()
    // 是否请求了中断线程
    bool QThread::isInterruptionRequested() const
    
    // 使用方法
    // 当中断线程时,外部调用
    thread->requestInterruption();
    // 在线程的任务循环中添加判断
    void long_task() {
      forever {
         if ( QThread::currentThread()->isInterruptionRequested() ) {
             return;
         }
     }
 }

三、处理异常情况程序退出后却仍然在后台挂起

在接收到终止信号时,需要退出线程并且退出主线程,可能会因为子线程耗时太长导致程序在后台挂起的情况(可能是这个原因),那么就需要先退出子线程,再退出主线程

点击折叠或展开代码
// 管理线程列表
QList<QPointer<QThread>> mThreadList;
// 创建智能指针
QPointer<QThread> thread = new QThread;
MyObject *obj = new MyObject;
// 移入线程
obj->moveToThread(thread);
// 连接信号和槽
connect(thread, &QThread::finished, obj, &MyObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// 其他可能的线程正常退出的情况,自定义结束信号finished
connect(obj, &MyObject::finished, [=](){
	// TODO
   if (! thread.isNull) {
   	thread->quit();
   	thread->wait();
   }
});

// 程序结束时,循环阻塞,线程全退出后主线程退出
while (mThreadList.size() > 0) {
       QPointer<QThread> thread = mThreadList.first();
       if ( ! thread.isNull()) {
           thread->quit();
           thread->wait();
       }
       mThreadList.pop_front();
   }
qApp.exit();
posted @ 2023-11-01 10:51  薄暮知秋  阅读(973)  评论(0编辑  收藏  举报
/*https://www.cnblogs.com/lingr7/p/15651906.html*/ /*自动显示目录导航*/