当前位置: 首页 > news >正文

50、继承方式创建QThread---------多线程

继承方式创建QThread

这种方法通过继承 QThread 类并重写其 run() 方法来实现线程逻辑。run() 方法中可以包含需要在线程中执行的代码。
我们看一下QThread的构造函数

explicit QThread(QObject *parent = nullptr);

所以子类继承QThread,子类构造也写成显示构造,并且参数为QObject*即可。

QThread有一个run函数,我们需要在子类中重写

virtual void run();

线程启动函数为, 不传递参数就是默认启动

[slot] void QThread::start(QThread::Priority priority = InheritPriority)

传递参数 就是设置优先级, QT提供了几种设置优先级的方式

以下是QThread::Priority枚举的常量:

  1. QThread::IdlePriority: 表示空闲优先级。线程将仅在系统空闲时运行。
  2. QThread::LowestPriority: 表示最低优先级。线程的优先级低于正常优先级。
  3. QThread::LowPriority: 表示低优先级。线程的优先级低于正常优先级,但高于最低优先级。
  4. QThread::NormalPriority: 表示正常优先级。线程的默认优先级。
  5. QThread::HighPriority: 表示高优先级。线程的优先级高于正常优先级。
  6. QThread::HighestPriority: 表示最高优先级。线程的优先级高于所有其他优先级。
  7. QThread::TimeCriticalPriority: 表示时间关键优先级。此优先级用于需要及时处理的线程。
  8. QThread::InheritPriority: 表示继承优先级。线程将继承其父线程的优先级。

线程启动后是在后台运行的,如果主线程退出,也就代表了主进程退出,此时子线程也会被回收,所以有时候需要在主线程中等待子线程执行完,主线程再退出。

等待子线程退出,需要调用如下函数

bool wait(unsigned long time = ULONG_MAX)

wait函数如果不传递参数表示无限制的等待直到线程运行完成

否则传递的参数表示最多等待指定毫秒时间

特别注意

因为我们自己定义的线程类需要发送信号,根据前面学过的元对象机制,我们需要在自己定义的类里添加Q_OBJECT宏声明

所以我们自定声明的线程类应该是这个样子

#include <QThread> class WorkerThread : public QThread { Q_OBJECT public: //显示构造,并且指定父节点 explicit WorkerThread(QObject *parent = nullptr); //要重写的run函数 virtual void run() override; };

接下来实现构造函数

WorkerThread::WorkerThread(QObject* parent):QThread(parent) { }

重写线程执行函数

void WorkerThread::run() { qDebug() << "线程开始:" << QThread::currentThreadId(); // 执行耗时任务 for(int i = 0; i < 5; ++i) { qDebug() << "计数:" << i; sleep(1); // 模拟耗时操作 } qDebug() << "线程结束:" << QThread::currentThreadId(); }

重写的函数里我们打印了5个数字,每打印1次睡眠1s。并且在开始和结束的时候打印了线程id。

然后我们在主函数中启动线程并且等待线程退出

#include <QApplication> #include "workerthread.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); //等待线程退出 thread.wait(); return a.exec(); }

程序输出

线程开始: 0x9fe0 计数: 0 计数: 1 计数: 2 计数: 3 计数: 4 线程结束: 0x9fe0

因为a.exec()阻塞,所以主线程没有退出。

进阶思考:阻塞UI

如果我把主函数调用改成启动mainwindow,然后在show()函数后调用thread.wait(),在线程运行期间,界面能响应点击事件吗?

int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); MainWindow mainwindow; mainwindow.show(); //等待线程退出 thread.wait(); return a.exec(); }

答案是子线程运行的时候,主界面不能响应点击事件。

这段代码运行后,会看到鼠标在主界面上转圈圈。直到子线程运行结束后,鼠标才恢复正常。

因为在线程运行期间,主线程会阻塞等待子线程thread.wait返回,进而不会走入a.exec(),a.exec()是执行事件循环的函数,

因为子线程运行期间,主线程没有执行事件循环,所以主线程不会响应ui事件。

修改优化

我们可以把等待子线程退出放在a.exec()之后即可

int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); MainWindow mainwindow; mainwindow.show(); //a.exec()阻塞执行事件轮询 int res = a.exec(); //等待线程退出 thread.wait(); return res; }

再谈事件轮询

a.exec()是执行事件轮询,而且是阻塞函数,内部执行死循环通过轮询的方式检测每隔QObject对象的事件队列,如果有未处理的就处理,直到所有的消息都处理完。

所以我们可以得出结论,只要一个对象继承了QObject,并且这个对象有任务或者事件未完成,这个exec()就不会返回。

进阶思考:后台运行不等待

如果我启动一个子线程后,主线程不等待,直接退出会怎样?

将main函数改为如下

int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); //等待任务执行完成退出 return a.exec(); }

上面的程序会等到子线程执行完退出,因为子线程是继承自QThread的类,在子线程有任务未完成时,主线程的exec()不会退出。

上面的程序输出

线程开始: 0x9e40 计数: 0 计数: 1 计数: 2 计数: 3 计数: 4 线程结束: 0x9e40

好的,那么我们接下来不调用a.exec()会怎么样呢?

代码改成如下

int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); //无窗口则直接退出 return 0; }

程序运行后会出现如下异常

这段报错的意思是主线程退出的时候回收子线程,但是子线程仍然在运行。

所以程序异常结束了。

使用心得

所以同学们使用多线程的时候一定要注意线程的生命周期,一定要等待子线程运行结束后主线程再退出,否则会出现主线程退出强制回收子线程,如果子线程未完成任务被强制回收会导致进程异常。

http://www.jsqmd.com/news/604531/

相关文章:

  • 2026新疆正规旅行社口碑十大排名:新疆靠谱旅行社推荐权威实测 - 企业推荐官【官方】
  • 还在手动逐字整理会议纪要浪费时间?2026年这3款会议纪要自动生成软件5分钟搞定万字稿
  • IDEA中使用Claude Code
  • 3种跨语言场景解决方案:用Translumo实现实时屏幕翻译自由
  • HarmonyOS6 - RcNumberBox 三方库插件尺寸系统与按钮布局深度剖析
  • 想找国内专业温变变色纱线厂家?看这! - 企业推荐官【官方】
  • 基于Vue的青年志愿者乡村服务管理系统[vue]-计算机毕业设计源码+LW文档
  • PPT救星!6个宝藏网站高效搞定演示 - 品牌测评鉴赏家
  • iOS / SwiftUI 输入法(键盘)布局处理总结(AI版)
  • 【Scratch×AI 系列 07】流程使用(下):从 planX 到可导入的 .sb3(打包与自检)
  • 基于Vue的社区老年人健康管理与服务预约网站[vue]-计算机毕业设计源码+LW文档
  • 国际上靠谱的温变变色纱线公司有哪些 - 企业推荐官【官方】
  • 直播录制从未如此简单:StreamCap 40+平台自动录制全攻略
  • AI 驱动网络钓鱼主导数据泄露的机理、风险与防御体系研究 —— 基于阿联酋预警事件的实证分析
  • 从0到1学会使用PageHelper
  • 跨越平台鸿沟:在非ROS环境中通过Rosbridge与ROS 2 Humble高效通信
  • 超高效!这款音视频转文字神器,让你告别手动输入!
  • 【工业级Python内存治理白皮书】:覆盖CPython 3.8–3.12的7层内存管控架构,含可落地的监控-预警-自愈SOP手册
  • AI显微镜-Swin2SR惊艳效果展示:JPG噪点去除+边缘重构真实案例
  • 3步解锁视频自由:B站m4s缓存转MP4全攻略
  • 国际靠谱温变变色纱线公司哪家强? - 企业推荐官【官方】
  • STM32驱动AW9523B IO扩展芯片:从寄存器映射到多设备管理实战
  • 解锁Windows 10的Android生态:3大革新功能让跨设备体验无缝融合
  • AssetStudio终极指南:从Unity游戏中提取3D模型、纹理和音频资源的完整教程
  • 【仅限前500名开放】自动驾驶C++算法性能审计清单(含17项ASAM OpenSCENARIO兼容性检测项+Clang-Tidy定制规则集)
  • 家长必看!专业自闭症康复机构大揭秘 - 品牌测评鉴赏家
  • Dockerfile从零入门:手把手教你打包Node.js应用,解决镜像构建的常见坑
  • CVPR2022逆向蒸馏(Reverse Distillation)源码解读与复现:从One-Class Embedding到异常图生成
  • 夜光荧光发光纱线源头厂家直供 高亮持久价优打造吸睛爆款 - 企业推荐官【官方】
  • 基于Vue的社区医疗公益服务系统[vue]-计算机毕业设计源码+LW文档