【Qt基本功修炼】Qt线程的两种运行模式

news2025/2/3 16:55:23

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):

QThread::run()
其中,

QThread::exec()

有了事件循环,子线程就可以像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::exit()

通过停止消息循环来停止线程,是一个异步操作。因为消息循环可能忙于执行某个函数,而无法处理退出消息,这就导致线程的退出时间不确定。所以,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模块,我们将在后面的文章进行讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1427475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tomcat 部署项目时 war 和 war exploded区别

在 Tomcat 调试部署的时候,我们通常会看到有下面 2 个选项。 是选择war还是war exploded 这里首先看一下他们两个的区别: war 模式:将WEB工程以包的形式上传到服务器 ;war exploded 模式:将WEB工程以当前文件夹的位置…

《Pandas 简易速速上手小册》第5章:Pandas 数据合并与重塑(2024 最新版)

文章目录 5.1 数据合并:Concatenate 和 Merge5.1.1 基础知识5.1.2 重点案例:客户订单数据合并5.1.3 拓展案例一:产品目录和销售数据合并5.1.4 拓展案例二:员工信息和部门数据合并 5.2 数据透视和重塑5.2.1 基础知识5.2.2 重点案例…

保姆级系列:各种打印机驱动的安装和使用

保姆级系列:各种打印机驱动的安装和使用 1.介绍2.下载3.安装4.实践教程5.总结 1.介绍 市面上打印机品牌和型号众多,打印机接口目前主要分为如下几种: 逻辑端口:TCP/IP、WSD、USB、LPT、COM 物理端口:RJ45、DB15母、US…

JDK1.8源码环境搭建介绍

目录 一、环境说明 1.1 JDK 1.8 1.2 IDEA 二、搭建过程说明 2.1 创建Java工程 2.2 源码准备 2.2.1 查找源码 2.2.2 解压源码到工程中 2.3 更新SDK 2.3.1 更新工程SDK 2.3.1.1 新建SDK 2.3.1.2 更新工程SDK 2.4 测试 2.4.1 解决报错问题 2.4.1.1 解决 sun.awt.UNI…

C#,桌面游戏编程,数独游戏(Sudoku Game)的算法与源代码

本文包括以下内容: (1)数独游戏的核心算法; (2)数独游戏核心算法的源代码; (3)数独游戏的部分题目样本; (4)适老版《数独》的设计原则…

Mirus TransIT-X2® 在RNAi干扰实验中性能数据展示

不同RNAi干扰途径示意图 基因沉默相关功能研究在分子和细胞生物学中发挥着重要作用,化学转染也在该研究领域扮演者重要角色。常见参与RNAi干扰途径的天然RNA分子包括: ★.小干扰 RNA (Small interfering RNAs, siRNA) :由双链 RNA(dsRNA)断裂…

第0章 Linux 基础入门

第0章 Linux 基础入门 RHCSA Red Hat Certified System Administrator 红帽认证系统管理员。 什么是计算机 计算机的组成: 控制器 运算器 存储器 输出设备 输入设备 计算机只能识别0和1,也就是二进制数。 为什么要学习Linux Linux 因其高效率…

零基础爬什么值得买的榜单——爬虫练习题目一(答二)

新问题总在解决老问题之后出现 引言原因正文 开整方法一代码运行效果 方法二代码运行结果 推荐 补充两个请求头的参数知识RefererUser-Agent 结尾 引言 今天心情不是很好 但是得更新呀 其实我写博客的一方面 也是希望大家能够监督我 让我尽量少情绪化 保持一个应有的速率做正确…

【VSCode 光标返回上一位置】

默认按键 Windows: Alt ← ;或者 鼠标侧键 Linux: Ctrl Alt - ;貌似数字键盘的减号没效果 Mac: Ctrl - 自定义修改方法: VSCode左下角 “管理 / Manage” “键盘快捷方式 / KeyBoard Shortcuts” 搜索 “前进 / Go Forward 或 后退 / Go Back” 双击需…

PySpark(二)RDD基础、RDD常见算子

目录 RDD RDD五大特性 RDD创建 RDD算子 常见的Transformation算子 map flatMap mapValues reduceByKey groupBy filter distinct union join intersection glom groupByKey groupByKey和reduceByKey的区别 ? sortBy sortByKey 常见的action算子 countByKey…

npm ERR! reason: certificate has expired(淘宝镜像过期)

npm ERR! request to https://registry.npm.taobao.org/yauzl/-/yauzl-2.4.1.tgz failed, reason: certificate has expired 今天在执行npm install命令时,报错百度了下是淘宝证书过期原因 解决方法一 执行下面两个命令再进行npm install即可 npm cache clean --…

LangChain结合通义千问的自建知识库

LangChain结合通义千问的自建知识库 在使用了通义千问API了之后,下一步就是构建知识库文档,使用了比较有名的LangChian,最后成果将自己的txt生成了知识向量库,最后我还把自己的论文生成了一个知识向量库,然后问他我的…

测试环境搭建整套大数据系统(一:基础配置,修改hostname,hosts,免密,时间同步)

一:使用服务器配置。 二:修改服务器名称hostname,hosts。 在 Linux 系统中,hostname 和 /etc/hosts 文件分别用于管理主机名和主机名解析。 在三台服务器上,分别执行以下命令。 vim /etc/hostnamexdso-hadoop-test-0…

Linux内核调参常用整理

一、【写在前面】 Linux内核调参是一个重要知识,这篇文章总结一下常见的参数用法。 调参位置在 /etc/sysctl.conf中或者sysctl.d 区别是: /etc/sysctl.conf 文件: 这是默认的主配置文件,包含了系统上所有的 sysctl 参数配置。所…

maven打包spring项目

常用的Maven命令如下 命令 说明mvn clean 清理Maven 项目。会删除目标路径(一般是target目录)Maven生成的打包文件,编译文件。mvn package 打包Maven项目,会生成jar 或者war文件。mvn test 执行test目录下的测试用例。mvn deploy 发布依赖到远端mvn site 生成…

Leetcode206:反转链表

一、题目 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表 示例: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]输入:head [1,2] 输出:[2,1]输入:head [] 输出&#xff1…

面试经典 150 题 -- 滑动窗口 (总结)

面试经典150题链接 面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台 209 . 长度最小的子数组 思路 : 滑动窗口的思想,取ij0,向后遍历j,记录前缀和[l,r]为s,如果s>target,那么左端点向右移动,直到s…

net 一台路由器如何让两个不同网段的终端可以通信。

# 终端设备自己设置就行了 # 路由器的设置 The device is running! #################################################### <Huawei> Feb 1 2024 21:21:09-08:00 Huawei %%01IFPDT/4/IF_STATE(l)[0]:Interface GigabitEt hernet0/0/0 has turned into UP state. <…

Linux系统——防火墙

一、防火墙的认识 引言 安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…

POI word操作,如何使表格单元格文本内容垂直/水平居中(两行代码解决)

垂直居中&#xff1a;cell本身就有垂直对齐的api&#xff0c;一行代码就够。 水平居中&#xff1a;一般方式行不通&#xff0c;网上苦找了许多方式&#xff0c;代码都太长了&#xff0c;&#xff0c;忒麻烦。于是我突发奇想&#xff0c;发现可以利用paragraph的水平居中对齐方…