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 ¶meter) { 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);
二、程序退出时如何退出正在运行的线程
- 在耗时的循环操作中添加标志位的判断,在退出时将标志位设置成退出标志位,比如上面的WorkThread1 示例中重写的run函数中的处理方式,如果层级太多,该用全局变量就使用全局变量
- 翻阅帮助文档发现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();
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性