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

qt之事件循环与线程的关系

先说重点,先了解几个重要的概念,

  • 事件调度器,该调度器的具体实现与操作系统相关,不同的操作系统具有不同的实现,例如linux系统下该调度器的实现为QEventDispatcherUNIX,而window下的他们的实现为QEventDispatcherWin32,他们的目录是QTDIR/5.15.2/Src/qtbase/src/corelib/kernel目录下寻找即可。该调度器实现了事件循环的核心功能。不同操作系统的事件调度器他们的基类是同一个QAbstractEventDispatcher。
  • threadData,这里就称它为线程数据吧,每个QObject都有这样一个数据成员,从这里可以看出每个QObject对象都是线程相关的。threadData包含了该对象所属线程的相关信息,其中包含了该线程的事件队列等,该数据成员可以通过qobject::movetoThread来实现转移。
  • 每个线程可以启动多个事件循环(QEventLoop),但是所有的QEventLoop共享同一个事件调度器,所以在一个线程中即使启动多个事件,最终的事件循环中的事件调度器是同一个。
  • 事件循环的主要功能:我自身的理解是遍历本线程的事件队列,将消息队列中的事件发送到对应对象进行处理,该事件队列的消息可以是用户发送的自定义是事件,也可以是定时器事件,也可以是对象删除删除,也可以是操作系统的发出的一些消息转换而来的事件(例如按钮的点击操作等)。
  • 事件队列中存在事件压缩的概念,压缩的概念是将事件队列中相同事件id(比如定时器事件),做相同事情的事件压缩为1个事件。

话不多说,直接上源码,清晰的将事件循环与线程的关系通过代码的方式展现出来

主线程的事件循环是由下面的代码触发

QCoreApplication::exec()

exec的内部实现如下:

int QCoreApplication::exec()
{
...QEventLoop eventLoop;int returnCode = eventLoop.exec();
...return returnCode;
}

通过上面的代码可以看到,定义了一个QEventLoop对象,对该对象调用了exec方法。QEventLoop的exec简要实现如下:

int QEventLoop::exec(ProcessEventsFlags flags)
{Q_D(QEventLoop);
...while (!d->exit)processEvents(flags | WaitForMoreEvents | EventLoopExec);
...return d->returnCode;
}

由上可以看出,QEventLoop下的exec调用了processEvents方法。该方法的实现如下:

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{Q_D(QEventLoop);if (!d->threadData->eventDispatcher)return false;if (flags & DeferredDeletion)QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);return d->threadData->eventDispatcher->processEvents(flags);
}

由上可以看到在processEvent的实现中,首先判断了threadData中成员eventDispathcer是否存在,若不存在,则返回false,eventDispatcher从字面意思理解,是一个事件分发器,通过该对象指针调用其方法processEvents,到这里会有一个疑问,eventDispatcher是何时创建的呢?请看下面的代码:

QEventLoop::QEventLoop(QObject *parent): QObject(*new QEventLoopPrivate, parent)
{Q_D(QEventLoop);if (!QCoreApplication::instance() && QCoreApplicationPrivate::threadRequiresCoreApplication()) {qWarning(
"QEventLoop: Cannot be used without QApplication");} else {d->threadData.loadRelaxed()->ensureEventDispatcher();}
}
QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
{Q_UNUSED(data);
#if defined(Q_OS_DARWIN)bool ok = false;int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok);if (ok && value > 0)return new QEventDispatcherCoreFoundation;elsereturn new QEventDispatcherUNIX;
#elif !defined(QT_NO_GLIB)const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();if (qEnvironmentVariableIsEmpty(
"QT_NO_GLIB")&& (isQtMainThread || qEnvironmentVariableIsEmpty(
"QT_NO_THREADED_GLIB"))&& QEventDispatcherGlib::versionSupported())return new QEventDispatcherGlib;elsereturn new QEventDispatcherUNIX;
#elsereturn new QEventDispatcherUNIX;
#endif
}

 

可以看到,eventDispatcher是在QEventLoop对象构造的过程中创建的,注意在这里生成的eventDispatcher已经是QEventDispatcherUnix类型了。下一步就是列出事件调度器的核心实现了,这里以linux为里介绍,所以QEventDispatcherUNIX下的实现processEvents实现,源码如下所示:

bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{Q_D(QEventDispatcherUNIX);……QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);……timespec *tm = nullptr;……d->pollfds.append(d->threadPipe.prepare());int nevents = 0;switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {……}return (nevents > 0);
}void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,QThreadData *data)
{……while (i < data->postEventList.size()) {……const QPostEvent &pe = data->postEventList.at(i);++i;……QEvent *e = pe.event;QObject * r = pe.receiver;……QCoreApplication::sendEvent(r, e);}……
}

通过上面的代码可以看出,在事件调度器里面的processEvents实现中,首先调用sendPostedEvents方法将事件队列中的事件通过QCoreApplication::sendEvent调用发送给对应的对象,这里简要的提一下,sendevent方法是一个阻塞型的方法调用,仅用于本线程内的调用,他的内部内部核心实现是函数调用,即通过接收对象receiver的指针调用该对象的event方法,但是需要注意的是在调用event方法之前首先要调用该对象注册的事件监听器对象eventfilter方法。我会单独列出一个文章讲解sendevent,详细实现将在里面实现。

然后后调用了poll相关的方法,里面射击了threadPipe的相关用法,我通过查看资料了解到的是下面的实现会导致该循环让出cpu,等待事件队列中有属于再进行工作,这一块我需要再研究下后续补充。

总结

至此,我们了解了一个事件循环的本质及与事件循环与线程的关系:

  1. 事件循环与线程密不可分,虽然在一个线程中可以启动多个事件循环,但是这些事件循环共享同一个threadData,也就是说在一个线程内无论启动多少个事件循环,他们操作的是都是同一个事件调度器,并不会因为启动多个事件队列,而导致别的事件队列无法接收数据。
  2. 事件循环的本质就是将事件队列中的事件一一发送到本线程中的各个对象中进行处理
  3. 每个QObject对象都是线程相关的,每个QObject对象都存在一个threadData成员,这个成员包含了这个对象所属的线程信息


 

相关文章:

qt之事件循环与线程的关系

先说重点&#xff0c;先了解几个重要的概念&#xff0c; 事件调度器&#xff0c;该调度器的具体实现与操作系统相关&#xff0c;不同的操作系统具有不同的实现&#xff0c;例如linux系统下该调度器的实现为QEventDispatcherUNIX&#xff0c;而window下的他们的实现为QEventDis…...

Python 变量的定义和数据类型的转换

变量 变量的定义 基本语法&#xff1a;变量名 值 变量名是给对象贴一个用于访问的标签&#xff0c;给对象绑定名字的过程也称为赋值&#xff0c;赋值符号 “” 变量名自定义&#xff0c;要满足标识符命名规则。 Python中&#xff0c;不需要事先声明变量名及其类型&#xff…...

Android Java JVM常见问答分析与总结

一、JVM是什么 JVM是JavaVirtualMachine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 JVM的重要性 JVM这块是一个偏向于概念模…...

【业务功能篇102】springboot+mybatisPlus分页查询,统一返回封装规范

业务场景&#xff1a; 随着业务代码量增多&#xff0c;很多接口查询的分页写法各种各样&#xff0c;为了使项目工程代码易于维护&#xff0c;我们统一规范&#xff0c;相对没有那么复杂的接口&#xff0c;我们统一都在java的service实现类中&#xff0c;去完成分页查询的接口逻…...

中国手机新进程:折叠屏出海的荣耀,5G中回归的华为

最近&#xff0c;“华为5G回归”“自研麒麟芯片回归”的消息引爆网络。网友开心庆贺之余&#xff0c;也纷纷猜测&#xff0c;华为强势归来&#xff0c;哪家友商最慌&#xff1f; “华为的回归&#xff0c;让竞争充满了更多的可能性和更多的魅力”&#xff0c;与华为渊源颇深的…...

安装RabbitMQ的各种问题(包括已注册成windows服务后,再次重新安装,删除服务重新注册遇到的问题)

一、安装Erlang&#xff08;傻瓜式安装&#xff09; 安装完成之后&#xff0c;配置环境变量&#xff1a; 1.新建系统变量名为&#xff1a;ERLANG_HOME 变量值为erlang安装地址 2. 双击系统变量path&#xff0c;点击“新建”&#xff0c;将%ERLANG_HOME%\bin加入到path中。 …...

多线程与高并发——并发编程(6)

文章目录 六、并发集合1 ConcurrentHashMap1.1 存储结构1.2 存储操作1.2.1 put方法1.2.2 putVal方法-散列算法1.2.3 putVal方法-添加数据到数组&初始化数组1.2.4 putVal方法-添加数据到链表1.3 扩容操作1.3.1 treeifyBin方法触发扩容1.3.2 tryPresize方法-针对putAll的初始…...

Elasticsearch——Docker单机部署安装

文章目录 1 简介2 Docker安装与配置2.1 安装Docker2.2 配置Docker镜像加速器2.3 调整Docker资源限制 3 准备Elasticsearch Docker镜像3.1 下载Elasticsearch镜像3.2 自定义镜像配置3.3执行Docker Compose 4 运行Elasticsearch容器4.1 创建Elasticsearch容器4.2 修改配置文件4.3…...

基于AHP模型指标权重分析python整理

一 背景介绍 日常会有很多定量分析的场景&#xff0c;然而也会有一些定性分析的场景针对定性分析的场景&#xff0c;预测者只能通过主观判断分析能力来推断事物的性质和发展趋势然而针对个人的直觉和虽然能够有一定的协助判断效果&#xff0c;但是很难量化到指标做后期的复用 …...

用python实现基本数据结构【02/4】

*说明 如果需要用到这些知识却没有掌握&#xff0c;则会让人感到沮丧&#xff0c;也可能导致面试被拒。无论是花几天时间“突击”&#xff0c;还是利用零碎的时间持续学习&#xff0c;在数据结构上下点功夫都是值得的。那么Python 中有哪些数据结构呢&#xff1f;列表、字典、集…...

蓝牙Mesh专有DFU

蓝牙Mesh专有DFU Mesh专有DFU协议介绍特征DFU模式和类型角色并发传输混合设备的网络传输速率后台操作传输分区内存映射安全DFU固件IDApplication firmware IDSoftDevice firmware IDBootloader firmware ID 设备页面格式内容 Mesh专有DFU协议介绍 设备固件更新(Device Firmwar…...

浅谈综合管廊智慧运维管理平台应用研究

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;为提升综合管廊运维管理水平&#xff0c;实现管理的数字化转型&#xff0c;采用综合监测系统、BIMGIS 可视化系统、智能机器人巡检、结构安全监测等技术&#xff0c;搭建实时监控、应急管理、数据分析等多功能…...

Httpservletrequest与Httpservletresponse

目录 一、Httpservletrequest 1.1什么是Httpservletrequest 1.2Httpservletrequest中的方法 二、Httpservletresponse 1.1什么是Httpservletresponse 1.2Httpservletresponse的方法 一、Httpservletrequest 1.1什么是Httpservletrequest HttpServletRequest&#xff08;…...

文件上传之图片码混淆绕过(upload的16,17关)

目录 1.upload16关 1.上传gif loadup17关&#xff08;文件内容检查&#xff0c;图片二次渲染&#xff09; 1.上传gif&#xff08;同上面步骤相同&#xff09; 2.条件竞争 1.upload16关 1.上传gif imagecreatefromxxxx函数把图片内容打散&#xff0c;&#xff0c;但是不会…...

Jetsonnano B01 笔记5:IIC通信

今日继续我的Jetsonnano学习之路&#xff0c;今日学习的是IIC通信&#xff0c;并尝试使用Jetson读取MPU6050陀螺仪数据。文章提供源码。文章主要是搬运的官方PDF说明&#xff0c;这里结合自己实际操作作笔记。 目录 IIC通信&#xff1a; IIC硬件连线&#xff1a; 安装IIC库文…...

【网络爬虫笔记】爬虫Robots协议语法详解

Robots协议是指一个被称为Robots Exclusion Protocol的协议。该协议的主要功能是向网络蜘蛛、机器人等搜索引擎爬虫提供一个标准的访问控制机制&#xff0c;告诉它们哪些页面可以被抓取&#xff0c;哪些页面不可以被抓取。本文将进行爬虫Robots协议语法详解&#xff0c;同时提供…...

MATLAB 2022b 中设置关闭 MATLAB 之前进行询问

在 MATLAB 2022b 中可以进行设置&#xff0c;在关闭 MATLAB 之前进行询问&#xff0c;防止意外关闭 MATLAB。如图&#xff1a;...

在SpringBoot框架下,接口有读个实现类,在不改变任何源码的情况下,SpringBoot怎么知道给接口注入哪个实现类的依赖呢?

在Spring Boot框架下&#xff0c;当一个接口有多个实现类时&#xff0c;Spring Boot 默认情况下不知道要注入哪个实现类的依赖。因此&#xff0c;你需要使用一些方法来明确告诉Spring Boot应该注入哪个实现类的依赖。 以下是一些常用的方法&#xff1a; 1.使用Qualifier注解&a…...

探索数据库管理的利器 - PHPMyAdmin

有一个项目&#xff0c;后端由博主独自负责&#xff0c;最近需要将项目交接给另一位同事。在项目初期&#xff0c;博主直接在数据库中使用工具创建了相关表格&#xff0c;并在完成后利用PhpMyAdmin生成了一份数据字典&#xff0c;供团队使用。然而&#xff0c;在随后的开发过程…...

大数据技术原理与应用学习笔记第1章

黄金组合访问地址&#xff1a;http://dblab.xmu.edu.cn/post/7553/ 1.《大数据技术原理与应用》教材 官网&#xff1a;http://dblab.xmu.edu.cn/post/bigdata/ 2.大数据软件安装和编程实践指南 官网林子雨编著《大数据技术原理与应用》教材配套大数据软件安装和编程实践指…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...