【Qt基本功修炼】Qt线程的两种运行模式
1. 前言
QThread是Qt中的线程类,用于实现多线程运行。
QThread有两种工作模式,即
- 消息循环模式
- 无消息循环模式
两种模式分别适用于不同的场景。下面我们将从多个方面,讲解QThread两种工作模式的区别。
2. 消息循环模式
2.1 实现原理
QThread::run中的代码是在子线程中运行的。
QThread::run是虚函数,从它的默认实现的中可以看到,在QThread::run中启动了一个QEventLoop,即事件循环。部分源码如下所示(文件路径Qt5.9.9\5.9.9\Src\qtbase\src\corelib\thread\qthread.cpp):

其中,

有了事件循环,子线程就可以像UI线程(即主线程)一样,进行消息处理。
2.2 使用方法
设worker是QObject子类的一个对象,具有信号和槽。如何让worker的槽函数运行于子线程中?可以使用 QObject::moveToThread()函数来将worker对象关联到某个子线程中。典型的写法如下:
QThread *thread = new QThread(this); // 线程对象
Worker *worker = new Worker(this); // 工人对象
worker->moveToThread(thread); // worker-thread 关联
thread->start(); // 启动线程
此后,通过Qt::AutoConnection/Qt::QueuedConnection/Qt::BlockQueuedConnection方式连接到worker的信号一旦被触发,Qt内部就会向worker所在子线程插入一条消息,等到消息循环处理到这条消息时,worker的槽函数会在子线程中被执行。
同理,若worker的某个信号通过Qt::AutoConnection/Qt::QueuedConnection/Qt::BlockQueuedConnection方式连接到主线程的一个对象的槽函数上,当此信号触发时,Qt也会向主线程的消息队列中插入一条消息。当主线程的消息循环处理到这条消息时,主线程对象的槽函数会在主线程中被执行。
简而言之,消息循环模式下,线程之间进行信号槽通信,通常都是由消息机制实现的,并且槽函数是在对象的关联线程中执行的。
2.3 线程退出方法
在消息循环模式下,通过调用QThread::quit() 或 QThread::exit() 方法,可以在对应线程的消息队列中插入一条退出消息,等到此条退出消息被处理时,QEventLoop::exec()返回,即消息循环退出。随后,QThread::run()函数也会退出,线程停止,线程资源被释放。相关Qt源码如下:


通过停止消息循环来停止线程,是一个异步操作。因为消息循环可能忙于执行某个函数,而无法处理退出消息,这就导致线程的退出时间不确定。所以,QThread::quit()通常和QThread::wait() 在主线程中搭配调用,以实现主线程等待子线程退出。等待时长可以由用户根据实际情况来设置。如果线程退出时间过长,则需要对耗时长的函数进行优化,以实现快速退出的效果。当然如果消息循环空闲,则退出消息会很快得到处理,消息循环和线程会很快退出。
3. 无消息循环模式
3.1 实现原理
用户继承 QThread并重载 QThread::run(),即可使默认的消息循环失效,用户将需要在子线程执行的代码全部写在重载的run()函数内。在这种模式下,只有重载的 run() 函数是在子线程中运行的。弄清楚这一点,才能准确判断一段代码的执行线程。
3.2 使用方法
示例代码如下:
class MyThread : public QThread
{
protected:virtual void run(){// process code here}
}
创建和启动线程:
MyThread *thread = new MyThread(this);thread->start();
3.3 线程退出方法
因为没有消息循环,所以通过抛退出消息来使线程退出的QThread::quit()函数会失效。有两种退出/停止线程的方法:强制退出和正常退出。
3.3.1 强制退出
使用 QThread::terminate() 函数,可以让操作系统强制停止线程的执行。 调用 QThread::terminate() 后,线程不一定会立即退出,退出时间取决于操作系统。主线程可以调用 QThread::wait() 来等待线程退出。
正如 Qt 文档中所说,强制退出线程会导致资源来不及释放和清理,这会导致软件不稳定,非必要不使用。
3.3.2 正常退出
如果想要让线程正常退出,那么就需要在 QThread::run() 函数中插桩检测停止标记。一旦停止标记为真,则停止当前的工作,做好现场清理和资源释放,然后令 QThread::run() 函数返回即可。同时对外提供停止接口,用于设置停止标记。
示例代码如下:
class MyThread : public QThread
{
protected:virtual void run(){do{// 插桩检测停止标记if (m_stop_flag)break;// process code here// 插桩检测停止标记if (m_stop_flag)break;// process code here// 插桩检测停止标记if (m_stop_flag)break;// process code here} while (0);// 清理现场和资源clean();}// 对外提供停止接口void stop(){m_stop_flag = true;}private:bool m_stop_flag = false;
}
调用stop()接口以后,线程不会立即退出,需要调用 QThread::wait() 来等待线程退出。通过控制插桩位置和频率,可以控制线程退出的速度。停止代码如下:
thread->stop(); // 停止线程
thread->wait(2000); // 等待线程退出,不超过2s
4. 结语
在项目中,我们需要根据实际需求选择正确的线程运行模式,合理地实现软件功能,同时提高导致软件的稳定性和可靠性。
以上是Qt线程的基本使用方法,可以满足基本的使用需求。但用起来还是稍显麻烦。在此基础上,Qt Concurrent模块提供了启动线程的其他简便方式及高级用法,但万变不离其宗,打牢基础以后,学习高级用法会非常简单。对于Qt Concurrent模块,我们将在后面的文章进行讲解。
相关文章:
【Qt基本功修炼】Qt线程的两种运行模式
1. 前言 QThread是Qt中的线程类,用于实现多线程运行。 QThread有两种工作模式,即 消息循环模式无消息循环模式 两种模式分别适用于不同的场景。下面我们将从多个方面,讲解QThread两种工作模式的区别。 2. 消息循环模式 2.1 实现原理 Q…...
三、设计模式相关理论总结
一、面向对象编程 1.1 概述 简称Object Oriented Program(OOP),指以类或对象作为基础组织单元,遵循封装、继承、多态以及抽象等特性,进行编程。其中面向对象不一定遵循封装、继承、封装和多态等特性,只是前人总结的套路规范&…...
鸿蒙 WiFi 连接 流程
那当界面上显示扫描到的所有Ap时,我们选择其中的一个Ap发起连接,看下代码流程是怎样的。 // applications/standard/settings/product/phone/src/main/ets/model/wifiImpl/WifiModel.tsconnectWiFi(password: string) {let apInfo this.userSelectedAp…...
golang 创建unix socket http服务端
服务端 package mainimport ("fmt""net""net/http""os" )func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte("hello"))})http.HandleFunc("/world", …...
annaconda如何切换当前python环境
annaconda默认的python环境是base: 把各种项目的依赖都安装到base环境中不是一个好的习惯,比如说我们做爬虫项目和做自动化测试项目等所需要的依赖是不一样的,我们可以将为每个项目创建自己的环境,在各自的环境中安装自己的依赖&…...
gtkmm 与 Cambalache 与 Gtk::Builder (新手向)_
文章目录 前言Cambalache检查Xml.cpp文件如何写才能显示UI首先creat获取ui里的对象显示 前言 新手刚刚使用时的笔记 Cambalache检查Xml 窗口右键inspect UI Definition切换到Xml视图, 可以全选复制粘贴到你的ui文件里, Cambalache 只能保存为.cmb工程文件, 导出也不知道导出…...
uniapp小程序端使用计算属性动态绑定style样式踩坑
踩坑点: 使用uniapp编译小程序端动态绑定复杂style使用计算属性方式,return必须返回json字符串格式,不能返回object,否则会不起作用。 代码总览 视图层 逻辑层(注意这里是使用的计算属性哈) 这里我封装成了一个个性化…...
计算机网络概念、组成、功能和分类
文章目录 概要1.怎么学习计算机网络2.概念3.功能、组成4.工作方式、功能组成5.分类 概要 概念、组成、功能和分类 1.怎么学习计算机网络 2.概念 通信设备:比如路由器、路由器 线路:将系统和通信设备两者联系的介质之类的 计算机网络是互连的、自治的的计…...
MyBatisPlus基础操作之增删改查
目录 一、基本使用 1.1 插入数据 1.2 删除操作 1.3 更新操作 二、条件构造器Wrapper 2.1 常用AbstractWrapper方法 2.1.1 示例一 2.2.2 示例二 2.2.3 示例三 2.2 常用QueryWrapper方法 2.2.1 示例一 2.2.2 示例二 2.2.3 示例三(常用) 2.3 常…...
视频处理学习笔记1:YUYV422、NV12和h264
最近因为工作关系在恶补视频相关知识点,在此做一记录便于日后复习。 以下均是个人学习经验总结,可能存在错误和坑,欢迎大佬指教。 工作中用到的是YUYV422存储格式。存储的就是裸流YUYV422格式文件。 YUYV422是两个像素点共用一个UV分量&am…...
CTFshow web(命令执行29-36)
?ceval($_GET[shy]);­passthru(cat flag.php); #逃逸过滤 ?cinclude%09$_GET[shy]?>­php://filter/readconvert.base64-encode/resourceflag.php #文件包含 ?cinclude%0a$_GET[cmd]?>&cmdphp://filter/readconvert.base64-encode/…...
PyTorch深度学习实战(23)——从零开始实现SSD目标检测
PyTorch深度学习实战(23)——从零开始实现SSD目标检测 0. 前言1. SSD 目标检测模型1.1 SSD 网络架构1.2 利用不同网络层执行边界框和类别预测1.3 不同网络层中默认框的尺寸和宽高比1.4 数据准备1.5 模型训练 2. 实现 SSD 目标检测2.1 SSD300 架构2.2 Mul…...
【附代码】NumPy加速库NumExpr(大数据)
文章目录 相关文献测试电脑配置数组加减乘除数组乘方Pandas加减乘除总结 作者:小猪快跑 基础数学&计算数学,从事优化领域5年,主要研究方向:MIP求解器、整数规划、随机规划、智能优化算法 如有错误,欢迎指正。如有…...
4、安全开发-Python-蓝队项目流量攻击分析文件动态监控图片隐写技术
用途:个人学习笔记,有所借鉴,欢迎指正! 总结: (1)使用python脚本Scapy库实现指定网卡的流量抓包分析 (2)使用python脚本Watchdog实现指定目录文件行为监控 (…...
MySQL 日志管理
4.6)日志管理 MySQL 支持丰富的日志类型,如下: 事务日志:transaction log 事务日志的写入类型为 "追加",因此其操作为 "顺序IO"; 通常也被称为:预写式日志 write ahead…...
Python CSV文件读取和写入
本文主要为Python 实现CSV文件读取和写入操作。 CSV文件写入和读取 因为没有现成的csv文件,所以csv的顺序为先写入后读取。 写入 创建csv文件并把数据写入,有两种实现方式:直接插入所有行和插入单行。 示例如下: import csv i…...
如何使用C#调用LabVIEW算法
新建一个工程 这是必须的; 创建项目 项目 点击完成; 将项目另存为;方便后续的使用; 创建 一个测试VI 功能很简单,用的一个加法;将加数A,B设置为输入,和C设置为输出,…...
调用百度文心AI作画API实现中文-图像跨模态生成
作者介绍 乔冠华,女,西安工程大学电子信息学院,2020级硕士研究生,张宏伟人工智能课题组。 研究方向:机器视觉与人工智能。 电子邮件:1078914066qq.com 一.文心AI作画API介绍 1. 文心AI作画 文…...
JAVA SpringBoot中使用redis的事务
目录 一、Java语言介绍 二、SpringBoot框架介绍 三、Redis缓存介绍 四、什么是redis的事务 一、Java语言介绍 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司于1995年推出。它的设计目标是要求“一次编写,到处运行”(Write Once, Run Anywhere, WOR…...
docker部署自己的网站wordpress
目录 安装 1.创建目录 2.创建并启动mysql 3.创建并启动wordpress 使用 1.设置语言 2.设置基础信息 3.首页 安装 1.创建目录 mkdir -p /opt/wordpress/{db,data} 2.创建并启动mysql docker run -d --name my_mysql --restart always -e MYSQL_ROOT_PASSWORD123456 -e …...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
