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

Qt线程的两种使用方式

Qt线程介绍

C/C++程序都是从main函数入手的,Qt程序也不例外,在Qt中每个程序启动后拥有的第一个线程是GUI线程,也称主线程或者main线程,主线程负责几乎所有GUI操作,Qt中所有的组件类只能工作在GUI线程。而对于非GUI的耗时操作,都由QThread及其子类来完成,可以称为工作线程或者子线程。Qt中使用QThread类提供不依赖平台的管理线程的方法,如果要设计多线程程序,一般是从QThread继承定义一个线程类,在自定义线程类里进行任务处理。注意:只有GUI线程可以访问和操作窗体上的部件(控件),如果其他线程尝试直接访问这些控件,会导致程序崩溃。为了在线程中更新UI,可以使用信号和槽机制。

在Qt框架中,多线程编程是通过QThread类来实现的。QThread类提供了一种高级的、面向对象的方式来处理线程,它允许开发者将耗时的任务从主线程(GUI线程)中分离出来,以避免界面冻结和提高应用程序的响应性。

线程使用方式

在Qt中,常用的使用线程的方法有如下两种:

  • 继承QThread类,重写run()函数;

  • 继承QObject类,通过moveToThread(thread),交给thread执行。

另一种方式

在 Qt 框架中,重写 run 方法moveToThread是实现多线程的两种常用方式,分别对应不同的线程模型。以下详细解析两者的用法、区别及适用场景,并提供示例代码:

一、重写 QThread::run() 方法

QThread 是 Qt 中管理线程的核心类,其 run() 方法是线程的入口点(类似 main 函数)。通过继承 QThread 并重写 run(),可以定义线程的核心执行逻辑。

原理
  • QThread 对象本身属于创建它的线程(通常是主线程),但 run() 方法在新线程中执行。
  • 若 run() 中调用 exec(),会启动线程的事件循环,支持信号槽机制;若不调用,则线程执行完 run() 后直接退出。
示例代码
#include <QThread>
#include <QDebug>// 自定义线程类,继承 QThread
class MyThread : public QThread {Q_OBJECT  // 启用信号槽(需在 .pro 中添加 QT += core)
protected:// 重写 run 方法,定义线程执行逻辑void run() override {qDebug() << "线程开始执行,线程 ID:" << QThread::currentThreadId();// 模拟耗时操作(实际开发中避免使用 sleep,此处仅为示例)for (int i = 0; i < 5; ++i) {qDebug() << "执行任务" << i;msleep(1000);  // 线程休眠 1 秒(QThread 提供的线程安全休眠)}// 若需要事件循环,可调用 exec()// exec();  // 启动事件循环,线程会一直运行直到调用 quit()}
};// 使用方式
int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);qDebug() << "主线程 ID:" << QThread::currentThreadId();MyThread thread;thread.start();  // 启动线程(会调用 run())// 等待线程结束(可选,避免主线程提前退出)thread.wait();qDebug() << "线程执行完毕";return app.exec();
}
适用场景
  • 线程逻辑简单,无需与其他对象通过信号槽频繁交互。
  • 不需要线程长期运行(执行完任务后自动退出)。

二、moveToThread 方法

moveToThread 是 QObject 的成员函数,用于将一个 QObject 及其子对象 “移动” 到指定的 QThread 中,使该对象的槽函数和事件处理在目标线程中执行。

原理
  • 不继承 QThread,而是将业务逻辑封装在 QObject 子类中,通过 moveToThread 关联线程。
  • 线程的事件循环由 QThread::exec() 启动,信号槽自动在对应线程中执行(跨线程信号槽通过 Qt 的事件机制调度)。
示例代码
#include <QThread>
#include <QObject>
#include <QDebug>// 业务逻辑类(封装线程要执行的任务)
class Worker : public QObject {Q_OBJECT
public slots:// 槽函数:在目标线程中执行void doWork() {qDebug() << "任务开始执行,线程 ID:" << QThread::currentThreadId();// 模拟耗时操作for (int i = 0; i < 5; ++i) {qDebug() << "处理数据" << i;QThread::msleep(1000);}emit workFinished();  // 任务完成,发送信号}signals:void workFinished();  // 任务完成信号
};// 使用方式
int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);qDebug() << "主线程 ID:" << QThread::currentThreadId();// 创建线程和工作对象QThread *thread = new QThread;Worker *worker = new Worker;// 将工作对象移动到线程中worker->moveToThread(thread);// 连接信号槽:启动线程后执行任务QObject::connect(thread, &QThread::started, worker, &Worker::doWork);// 任务完成后退出线程QObject::connect(worker, &Worker::workFinished, thread, &QThread::quit);// 线程退出后销毁线程和工作对象(避免内存泄漏)QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater);// 启动线程(会触发 started 信号,进而调用 doWork)thread->start();return app.exec();
}
关键注意事项
  1. moveToThread 后,不能在原线程中直接调用对象的方法(需通过信号槽触发),否则可能导致线程安全问题。
  2. 若对象有父对象,moveToThread 会失败(需先解除父对象关联)。
  3. 线程必须启动事件循环(exec()),否则槽函数无法执行(QThread::start() 后默认会在 run() 中调用 exec())。
适用场景
  • 复杂业务逻辑,需要多个对象在同一线程中协作。
  • 频繁使用信号槽进行跨线程通信(如 GUI 线程与后台线程交互)。
  • 符合 “单一职责原则”(线程管理与业务逻辑分离)。

三、两种方式的核心区别

维度重写 run() 方法moveToThread 方法
实现方式继承 QThread,重写 run()QObject 子类 + moveToThread 关联
线程与逻辑的耦合度高(线程类包含业务逻辑)低(线程与业务逻辑分离)
信号槽支持需手动启动事件循环(exec()自动支持(依赖线程事件循环)
灵活性适合简单场景,扩展困难适合复杂场景,可动态切换线程
线程安全需手动控制共享资源信号槽机制自动处理线程安全调度

四、最佳实践

  • 优先使用 moveToThread:它是 Qt 推荐的方式,更符合面向对象设计,避免了继承 QThread 带来的耦合问题。
  • 避免在 run() 中直接操作 GUI 组件:Qt 中 GUI 操作必须在主线程(UI 线程)执行,跨线程操作需通过信号槽。
  • 注意资源释放:线程和工作对象需正确关联 deleteLater,避免内存泄漏。

通过合理选择两种方式,可以高效实现 Qt 多线程编程,平衡性能与代码可维护性。

http://www.dtcms.com/a/410418.html

相关文章:

  • 怎么自己做礼品网站企业培训课程分类
  • 在 openEuler 上排查 Docker 同桥网络不通:从“全线超时”到定位容器没启动
  • vector深度求索(上)实用篇
  • 筛选选功能形网站建设东营网站设计
  • 创建一个网站的英文wordpress 版本查询
  • DevEco Studio 编辑器的使用
  • VSCode中Python库识别的设置(关于解决VSCode中Python库引入未下载的库不显示黄色虚线的问题)
  • 网站你懂我意思正能量app阿里云Windows建立WordPress
  • K8s学习笔记(八) K8s资源对象
  • 医疗数据集成的挑战,iPaaS 如何保障隐私与安全?
  • 【金仓数据库产品体验官】Windows 安装 KingbaseES V9R1C10 与 Oracle 兼容特性实战
  • 阿里云建站流程集约化网站建设
  • Classic McEliece:后量子密码的“元老”与NIST标准化的启示
  • 【论健康】健康的不可能三角
  • 19C数据库提示ORA-65096: 公用用户名或角色名无效(详细操作版)
  • 软考中级习题与解答——第十三章_数据库分析与设计(2)
  • tomcat升级操作
  • 广州高端品牌网站建设后台管理便捷蜜雪冰城推广软文
  • 基于 SpringBoot+Logicflow 的轻流程任务执行
  • 基础组合计数(三道例题)
  • ShardingSphere 与分库分表:分布式数据库中间件实战指南
  • 《三重AI协作交易系统:从技术债泥潭到毫秒级响应的实战笔记》
  • AI 赋能楼宇自控 DDC 系统:重构智慧建筑的核心引擎
  • 更改wordpress密码上海关键词优化排名哪家好
  • 最好的设计师网站wordpress 实例
  • IDEA 实现SpringBoot热部署(HotSwap和DevTools混用)
  • 《IDEA 2025 长效使用指南:2099 年有效期配置实战之JetBrains全家桶有效》​
  • IntelliJ IDEA / Android Studio 里直接跑 Cursor(不用来回切窗口)
  • HarmonyOS应用前后台状态切换
  • 网站建设app销售好做吗哪里长沙网站开发