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

QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法

1. 引言

        在现代应用程序开发中,多线程编程是一个关键技术,能够显著提高程序的效率和响应速度。Qt 是一个跨平台的 C++ 框架,其中 QThread 类是实现多线程编程的核心类。本文将深入详解 QThread 的基本概念、使用方法及其在实际应用中的重要性。

2. 基本概念

2.1 什么是 QThread?

        QThread 是 Qt 框架中的一个类,用于创建和管理线程。与标准库中的 std::thread 类似,QThread 提供了一种机制来执行并发任务,但它不仅限于此。QThread 还集成了 Qt 的信号和槽机制,使得线程间的通信更加方便和高效。

2.2 线程的生命周期

        一个线程的生命周期包括创建、启动、执行、结束几个阶段。QThread 类提供了一系列方法来控制和管理这些阶段:

  • start(): 启动线程,调用 run() 方法。
  • run(): 线程的工作函数,通常需要重载。
  • quit(): 让线程退出事件循环,但不终止线程。
  • terminate(): 强制终止线程,不推荐使用。
  • wait(): 等待线程结束。

3. 使用方法

3.1 继承 QThread 类

最常见的使用方式是通过继承 QThread 类并重载其 run() 方法。

3.1.1 示例代码

为了确保代码文件内容正确,请参考以下内容:

首先我们需要自定添加一个MyThread 类,会自动生成头文件和源文件,文件中代码会自动生成基础部分,其他需要手动编写,如下:

项目目录结构如下所示:

/my_project├── main.cpp├── mythread.h├── mythread.cpp└── my_project.pro

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QThread>
#include <QDebug>class MyThread : public QThread {Q_OBJECTpublic:MyThread() = default;~MyThread() = default;protected:void run() override {// 这里是线程的主要工作for (int i = 0; i < 5; ++i) {qDebug() << "Thread running:" << i;QThread::sleep(1);}}
};#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
//这里仅做简单示例

 

main.cpp如下: 


int main() {MyThread thread;thread.start();thread.wait(); // 等待线程结束return 0;
}

运行结果如下:

 

3.1.2 知识精华

上面是一个简单 的 QThread 类应用示例,这里需要说明的一点是:

        在 Qt 项目中,.pro 文件是项目配置的重要部分,它不会自动生成,你需要手动创建它并添加相应的配置。如果你使用的是 Qt Creator 工具,它会自动为你生成一个初始的 .pro 文件,但你需要根据项目的需要手动修改和添加内容。

上面示例需要手动在.pro 文件中添加和修改配置项:

QT += core
QT -= guiCONFIG += console
CONFIG -= app_bundleTEMPLATE = appSOURCES += main.cpp \mythread.cppHEADERS += mythread.h

如果没有手动添加以上配置,Qt Creator工具中是无法构建运行的。当前示例.pro文件如下

3.2 使用 Worker-Object 模式

        继承 QThread 并重载 run() 方法虽然简单直观,但并不是最佳实践。更推荐的方式是使用 Worker-Object 模式,将工作对象移到新线程中。

#include <QObject>
#include <QThread>
#include <QDebug>// Worker类继承自QObject,用于执行线程中的工作任务
class Worker : public QObject {Q_OBJECTpublic slots:// 槽函数,执行工作任务void doWork() {// 模拟耗时操作,每次循环等待1秒for (int i = 0; i < 5; ++i) {qDebug() << "Worker running:" << i;QThread::sleep(1); // 线程休眠1秒}}
};int main() {// 创建一个QThread对象,用于管理新线程QThread thread;// 创建一个Worker对象,执行具体的工作任务Worker worker;// 将Worker对象移到新线程中运行worker.moveToThread(&thread);// 连接QThread的started信号和Worker的doWork槽// 当线程启动时,将调用Worker的doWork槽函数QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork);// 连接Worker的doWork槽函数和QThread的quit槽// 当Worker完成工作后,将调用QThread的quit槽函数,退出线程事件循环QObject::connect(&worker, &Worker::doWork, &thread, &QThread::quit);// 连接QThread的finished信号和Worker的deleteLater槽// 当线程结束时,将调用Worker的deleteLater槽函数,删除Worker对象QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);// 启动线程,开始执行工作任务thread.start();// 等待线程结束thread.wait();// 返回0,表示程序正常结束return 0;
}

Worker 类定义

  • Worker 类继承自 QObject
  • doWork 是一个槽函数,用于执行具体的工作任务。在这里,它模拟耗时操作,通过循环和线程休眠来演示。

 main 函数

  • 创建一个 QThread 对象 thread,用于管理新线程。
  • 创建一个 Worker 对象 worker,用于执行具体的工作任务。
  • 使用 moveToThread 方法将 Worker 对象移到新线程中运行。
  • 使用 QObject::connect 方法连接信号和槽:
    • 当线程启动时,调用 Worker 的 doWork 槽函数。
    • 当 Worker 完成工作后,调用 QThread 的 quit 槽函数,退出事件循环。
    • 当线程结束时,调用 Worker 的 deleteLater 槽函数,删除 Worker 对象。
  • 调用 thread.start() 启动线程,开始执行工作任务。
  • 调用 thread.wait() 等待线程结束。
  • 返回 0,表示程序正常结束。

4. 信号和槽

QThread 充分利用了 Qt 的信号和槽机制,使得线程间通信更加方便和安全。

#include <QObject>
#include <QThread>
#include <QDebug>// Worker类继承自QObject,用于执行线程中的工作任务
class Worker : public QObject {Q_OBJECTpublic slots:// 槽函数,执行工作任务void doWork() {// 这里是工作任务的代码emit workDone(); // 任务完成后发出信号}signals:// 信号,表示工作已经完成void workDone();
};int main() {// 创建一个QThread对象,用于管理新线程QThread thread;// 创建一个Worker对象,执行具体的工作任务Worker worker;// 将Worker对象移到新线程中运行worker.moveToThread(&thread);// 连接Worker的workDone信号和QThread的quit槽// 当Worker完成工作后,将调用QThread的quit槽函数,退出线程事件循环QObject::connect(&worker, &Worker::workDone, &thread, &QThread::quit);// 连接QThread的finished信号和Worker的deleteLater槽// 当线程结束时,将调用Worker的deleteLater槽函数,删除Worker对象QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);// 启动线程,开始执行工作任务thread.start();// 在主线程中等待新线程结束thread.wait();// 返回0,表示程序正常结束return 0;
}

Worker 类定义

class Worker : public QObject {Q_OBJECTpublic slots:void doWork() {emit workDone(); // 任务完成后发出信号}signals:void workDone(); // 信号,表示工作已经完成
};
  • Worker 类继承自 QObject
  • doWork 是一个槽函数,用于执行具体的工作任务。在这里,它发出 workDone 信号,表示任务已经完成。
  • workDone 是一个信号,用于通知外部任务已经完成。

main 函数

int main() {QThread thread;Worker worker;worker.moveToThread(&thread);QObject::connect(&worker, &Worker::workDone, &thread, &QThread::quit);QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);thread.start();thread.wait(); // 等待线程结束return 0;
}
  • 创建一个 QThread 对象 thread,用于管理新线程。
  • 创建一个 Worker 对象 worker,用于执行具体的工作任务。
  • 使用 moveToThread 方法将 Worker 对象移到新线程中运行。
  • 使用 QObject::connect 方法连接信号和槽:
    • 当 Worker 发出 workDone 信号时,调用 QThread 的 quit 槽函数,退出事件循环。
    • 当线程结束时,调用 Worker 的 deleteLater 槽函数,删除 Worker 对象。
  • 调用 thread.start() 启动线程,开始执行工作任务。
  • 调用 thread.wait() 在主线程中等待新线程结束。
  • 返回 0,表示程序正常结束。

5. 线程安全

        多线程编程中,线程安全是一个重要问题。QThread 提供了一些机制来帮助实现线程安全,例如 QMutex, QSemaphore, QWaitCondition 等。

#include <QMutex>
#include <QThread>
#include <QDebug>// 全局互斥锁,用于保护共享资源
QMutex mutex;// SafeWorker类继承自QObject,用于执行线程中的工作任务
class SafeWorker : public QObject {Q_OBJECTpublic slots:// 槽函数,执行工作任务void doWork() {// 锁定互斥锁,保护共享资源mutex.lock();for (int i = 0; i < 5; ++i) {qDebug() << "SafeWorker running:" << i;QThread::sleep(1); // 线程休眠1秒,模拟耗时操作}// 解锁互斥锁mutex.unlock();}
};int main() {// 创建一个QThread对象,用于管理新线程QThread thread;// 创建一个SafeWorker对象,执行具体的工作任务SafeWorker worker;// 将SafeWorker对象移到新线程中运行worker.moveToThread(&thread);// 连接QThread的started信号和SafeWorker的doWork槽// 当线程启动时,将调用SafeWorker的doWork槽函数QObject::connect(&thread, &QThread::started, &worker, &SafeWorker::doWork);// 连接SafeWorker的doWork槽函数和QThread的quit槽// 当SafeWorker完成工作后,将调用QThread的quit槽函数,退出线程事件循环QObject::connect(&worker, &SafeWorker::doWork, &thread, &QThread::quit);// 连接QThread的finished信号和SafeWorker的deleteLater槽// 当线程结束时,将调用SafeWorker的deleteLater槽函数,删除SafeWorker对象QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);// 启动线程,开始执行工作任务thread.start();// 在主线程中等待新线程结束thread.wait();// 返回0,表示程序正常结束return 0;
}

全局互斥锁

QMutex mutex;

 创建一个全局的互斥锁 mutex,用于保护共享资源。

SafeWorker 类定义

class SafeWorker : public QObject {Q_OBJECTpublic slots:void doWork() {mutex.lock(); // 锁定互斥锁,保护共享资源for (int i = 0; i < 5; ++i) {qDebug() << "SafeWorker running:" << i;QThread::sleep(1); // 线程休眠1秒,模拟耗时操作}mutex.unlock(); // 解锁互斥锁}
};
  • SafeWorker 类继承自 QObject
  • doWork 是一个槽函数,用于执行具体的工作任务。在这里,通过锁定和解锁互斥锁来保护共享资源,确保线程安全。

main 函数

  • 创建一个 QThread 对象 thread,用于管理新线程。
  • 创建一个 SafeWorker 对象 worker,用于执行具体的工作任务。
  • 使用 moveToThread 方法将 SafeWorker 对象移到新线程中运行。
  • 使用 QObject::connect 方法连接信号和槽:
    • 当线程启动时,调用 SafeWorker 的 doWork 槽函数。
    • 当 SafeWorker 完成工作后,调用 QThread 的 quit 槽函数,退出事件循环。
    • 当线程结束时,调用 SafeWorker 的 deleteLater 槽函数,删除 SafeWorker 对象。
  • 调用 thread.start() 启动线程,开始执行工作任务。
  • 调用 thread.wait() 在主线程中等待新线程结束。
  • 返回 0,表示程序正常结束。

6. 实际应用中的重要性

        QThread 在实际应用中非常重要,特别是在需要进行耗时操作的场景下,如网络请求、数据库操作、大文件读写等。使用 QThread 可以显著提高应用程序的响应速度和用户体验。

7. 结论

        QThread 是 Qt 框架中一个强大且灵活的类,能够有效地实现多线程编程。通过本文的介绍,希望读者能够掌握 QThread 的基本概念、使用方法以及在实际应用中的重要性,从而更好地开发高性能的 Qt 应用程序。

8. 参考资料

  • Qt Documentation: QThread
  • Qt Documentation: Signals and Slots
  • Qt Documentation: Thread Support in Qt

相关文章:

QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法

1. 引言 在现代应用程序开发中&#xff0c;多线程编程是一个关键技术&#xff0c;能够显著提高程序的效率和响应速度。Qt 是一个跨平台的 C 框架&#xff0c;其中 QThread 类是实现多线程编程的核心类。本文将深入详解 QThread 的基本概念、使用方法及其在实际应用中的重要性。…...

【芋道源码】gitee很火的开源项目pig——后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)

后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09;前言一、如何获取项目&#…...

设计模式、系统设计 record part01

技术路线&#xff1a; 工程师》设计师》分析师》架构师 管理路线&#xff1a; 项目经理》技术经理 工程师&#xff1a; 编程技术、测试技术 设计师&#xff1a; 工程师设计技术 分析师&#xff1a; 设计师分析技术 架构师&#xff1a; 分析师架构技术 项目经理&#xff1a; 时间…...

服务器与普通电脑的区别是什么?

服务器作为企业进行线上业务所使用的网络设备&#xff0c;大多数的用户对于服务器都有一定的了解&#xff0c;而普通的电脑则是人们在进行日常娱乐活动中经常会用到的设备&#xff0c;本文就来探讨一下服务器与普通电脑之间的区别是什么吧&#xff01; 普通的电脑就是我们通常所…...

Vue3学习(六)Vue3 + ts几种写法

前言 官网提到组合式api和选项式api 选项式api其实就是vue2的写法&#xff0c;组合式api是vue3的新写法&#xff08;组合式api可以在script中使用setup&#xff08;&#xff09;也可以使用<script setup>&#xff0c;<script setup>是setup&#xff08;&#xff…...

【前端】ES6:Proxy代理和Reflect对象

文章目录 1 Proxy代理1.1 get方法1.2 set方法1.3 has方法1.4 this问题 2 Reflect对象2.1 代替Object的某些方法2.2 修改某些Object方法返回结果2.3 命令式变为函数行为2.4 配合Proxy 1 Proxy代理 Proxy如其名&#xff0c;它的作用是在对象和和对象的属性值之间设置一个代理&am…...

基于微信开发助手企鹅音乐微信小程序的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…...

学习Spring Boot,应该从哪里开始学起

文章目录 前言1. Java基础2. Spring框架基础3. Spring Boot入门4. 搭建Spring Boot项目5. 编写RESTful API6. 数据库操作7. 安全性和测试8. 部署和运维9. 实践和项目总结前言 学习Spring Boot,应该从哪里开始学起 学习Spring Boot,你可以从以下几个步骤开始学起: 1. Java基…...

【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题

前言&#xff1a; &#x1f308;上期博客&#xff1a;【后端开发】JavaEE初阶—线程安全问题与加锁原理&#xff08;超详解&#xff09;-CSDN博客 &#x1f525;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 ⭐️小编会在后端开发的学习中不断更新~~~ &#…...

企微群管理软件:构建高效社群运营的新引擎

在数字化营销日益盛行的今天&#xff0c;企业微信&#xff08;简称“企微”&#xff09;群作为企业与用户直接互动的重要平台&#xff0c;其管理与运营效率直接关系到企业的品牌形象、用户满意度及市场影响力。企微群管理软件&#xff0c;作为专为企微社群设计的高效管理工具&a…...

CORE 中间件、wwwroot

ASP.NET Core中间件组件是被组装到应用程序管道中以处理HTTP请求和响应的软件组件&#xff08;从技术上来说&#xff0c;组件只是C&#xff03;类&#xff09;。 ASP.NET Core应用程序中的每个中间件组件都执行以下任务。 选择是否将 HTTP 请求传递给管道中的下一个组件。这可…...

SpringBoot 与 Maven 快速上手指南

SpringBoot 与 Maven 快速上手指南 在Java开发领域&#xff0c;Spring Boot和Maven是两个极其重要的工具&#xff0c;它们极大地简化了企业级应用的开发和构建过程。Spring Boot通过自动配置和起步依赖等特性&#xff0c;让开发者能够快速搭建起一个Spring应用&#xff1b;而M…...

大觅网之自动化部署(Automated Deployment of Da Mi Network)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…...

【C++】入门基础知识-1

&#x1f36c;个人主页&#xff1a;Yanni.— &#x1f308;数据结构&#xff1a;Data Structure.​​​​​​ &#x1f382;C语言笔记&#xff1a;C Language Notes &#x1f3c0;OJ题分享&#xff1a; Topic Sharing 目录 前言&#xff1a; C关键字 命名空间 命名空间介…...

Redis一些简单通用命令认识常用数据类型和编码方式认识Redis单线程模型

通用命令 get() / set() 这是Redis中两个最为核心的命令。 set插入 这里的key 和 value都是字符串&#xff0c;我们可以加双引号 或者单引号&#xff0c;或者不加。 get查找 如果查询的key值不存在&#xff0c;那么会返回一个 nil &#xff0c;也就是代表空 在Redis中命令…...

使用电子模拟器 Wokwi 运行 ESP32 示例(Arduino IDE、VSCode、ESP32C3)

文章目录 Wokwi 简介安装客户端&#xff08;Mac/Linux&#xff09;创建 Token Arduino IDEVSCode 配置安装 wokwi 插件打开编译后目录 ESP32C3 示例Arduino IDE创建模拟器运行模拟器 Wokwi 简介 Wokwi 是一款在线电子模拟器。您可以使用它来模拟 Arduino、ESP32、STM32 以及许…...

C嘎嘎入门篇:类和对象(1)

前言&#xff1a; 小编在之前讲述了C的部分入门基础&#xff0c;读者朋友一定要掌握好那些&#xff0c;因为C的学习和C有点不同&#xff0c;C的知识都是比较连贯的&#xff0c;所以我们学好了前面才可以学习后面的内容&#xff0c;本篇文章小编将会讲述C真正的入门篇&#xff1…...

tomcat服务搭建部署ujcms网站

tomcat服务搭建部署ujcms网站 关闭selinux和防火墙 setenforce 0 && systemctl stop firewalld安装java环境 #卸载原有java8环境 yum remove java*#上传java软件包&#xff0c;并解压缩 tar -xf openjdk-11.0.1_linux-x64_bin.tar.gz && mv jdk-11.0.1 jdk11…...

unity_Occlusion_Culling遮挡剔除学习

unity_Occlusion_Culling遮挡剔除学习 文档&#xff1a; https://docs.unity.cn/cn/2019.4/Manual/occlusion-culling-getting-started.html没彻底搞明白&#xff0c;但是会用&#xff0c;虽然也不熟练 设置遮挡剔除 打开遮挡剔除面板 设置场景物体。设置为静态 设置场景 烘…...

vue初学随笔

Vue基础 Vue基本概念 Vue是什么 Vue是一个渐进式的JavaScript框架&#xff0c;它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。 渐进式&#xff1a;各个特性可以根据项目需要逐渐引入和…...

IDEA Dependency Analyzer 分析 maven 项目包的依赖

一、场景分析 javax.validation 是我们 SpringMVC 常用的数据校验框架。但是 javax.validation 是一个规范&#xff08;Java Bean Validation&#xff0c;简称 JSR 380&#xff09;&#xff0c;它并没有具体的实现&#xff0c;它的常用实现&#xff0c;是hibernate-validator。…...

微信小程序 - 最新详细安装使用 Vant weapp UI 框架环境搭建详细教程

前言 自从 2024 年开始,小程序做了很多改变和升级, 导致网上很多搭建教程文章的教程失效了,本文来做最新的教程。 第一步 为了更贴合新手,我这里创建了一个纯净无任何业务代码的小程序项目。...

【C语言】手把手带你拿捏指针(完)(指针笔试、面试题解析)

文章目录 一、sizeof和strlen的对⽐1.sizeof2.strlen3.sizeof与strlen对比 二、数组和指针笔试解析1.一维数组2.字符、字符串数组和字符指针代码1代码2代码3代码4代码5代码6 3.二维数组4.总结 三、指针运算笔试题解析代码1代码2代码3代码4代码5代码6 一、sizeof和strlen的对⽐ …...

Vue中input框自动聚焦

在Vue中input自动聚焦的思路&#xff1a; 给需要聚焦的input设置ref <el-inputv-model"loginForm.username"ref"userNameInput"name"username"type"text"auto-complete"on"placeholder"username"keyup.enter.…...

基于Node.js+Express+MySQL+VUE实现的计算机毕业设计旅游推荐网站

猜你喜欢评论 登录注册搜索 推荐定制景点/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序 功能图如下所示&#xff1a; 一、设计目标 本次计算机毕业设计项目的主要目标是设计和开发一款功能完善、用户友好的旅游推荐网站。该网站旨在为广大旅游爱好者提供一个便捷、…...

已存在的Python项目使用依赖管理工具UV

1. 文档 uv文档 2. 如何转换 初始化 uv initrequirements.txt转换成pyproject.toml uv add $(cat requirements.txt)删除requirements.txt 如果更新pyproject.toml之后&#xff0c;使用命令 uv sync替换项目环境 如果有库没有加入依赖&#xff0c;自己手动加一下&am…...

JavaWeb美食推荐管理系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 login.jsp 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优…...

如何像专家一样修复任何 iPhone 上的“iPhone 已禁用”错误

“我忘记了密码&#xff0c;并且我的 iPhone 在多次输入错误密码后就被禁用了&#xff0c;如何再次访问我的手机&#xff1f;” 作为最安全的数字设备之一&#xff0c;iPhone 必须使用正确的密码解锁。即使您可以使用 Face ID 或 Touch ID 访问您的设备&#xff0c;在充电或重…...

django drf to_representation

使用场景 1.需要对结果的中的某个字段进行进一步的处理&#xff1b; 2.模型类中没有的字段&#xff0c;添加到结果中进行响应&#xff1b; 例子 from django.db import modelsclass TestModel(models.Model):name models.CharField(_("名称"), max_length50, nul…...

2024最新国内镜像源设置(npm、yarn、pnpm)

淘宝镜像源https://registry.npmmirror.com/ 腾讯云镜像源https://mirrors.cloud.tencent.com/npm/ cnpm是一个基于npm的中国镜像源https://r.cnpmjs.org/ # 查询当前使用的镜像源 npm get registry# 设置为淘宝镜像源 npm config set registry https://registry.npmmirror.co…...