深度解析qt核心机制:信号槽的多线程行为与对象的线程依附性
对象的线程依附性
每一个学过C++以及系统编程的程序员,对于变量会与特定线程有关联都会感到不可思议;在qt中所说的对象的线程依附性,只是针对继承自QObject的对象而言的;对象的线程依附性,并不是代表真的某个底层线程才能访问这个变量而其他线程不行;而是一种qt实现逻辑上的标记需要;这个qt实现逻辑就是qt核心机制信号槽机制;
qt对象的线程依附性的真正含义是:这个对象只接收或者只处理所依附线程的事件队列里面的事件【有人会问这跟信号槽有什么关系?请先记住这句话!】
在qt中每一个线程都可以有一个唯一的事件队列【类似于windows里面的消息队列】,线程事件队列中接受存放过来的事件任务,这个线程也进行事件循环从事件队列中取出事件任务分派给对应的对象去处理【类似于消息循环分派消息给对应的窗口处理,但是qt中这时分派给对象处理】;注意这里分派给继承自QObject的对象处理;对象所处理的事件任务,一定是从对象所依附的线程的事件队列中取出的任务!
我们现在已经讲了 线程事件队列,线程事件循环,对象的线程依附性;现在来看看connect也就是信号槽的真正语义是什么;
无论采用何种策略,connect的主体语义只有二种
1.同一线程内直接调用:这时信号的触发或者说调用信号线程与槽函数的触发执行是同一线程;【无论这个emit是手动显示调用还是预定义信号底层通过消息事件触发的】对应的emit的语义就是单线程内的直接调用
2.不同线程间的一个线程存放事件任务到另一个线程的事件队列中:这时信号的触发(调用信号)的线程就是存放动作的发出者,由这个线程存放事件任务到接收者所依附线程的事件队列中;所以这时候emit的语义就是事件任务存放到事件队列!
这里有几个需要注意说明的点:
1.信号触发线程,或者是信号调用线程指的是执行(调用)emit【无论是显示还是隐式】的线程,而非connect 发送者对象所依附的线程!
2.接收者依附线程确实指的是接收者对象所依附的线程
一般而言对象所依附的线程是创建这个对象时【即调用这个对象的构造函数】所在的线程!后面这个对象可以被moveToThread依附到其他线程,但是执行这个操作时需要注意,调用执行这个moveToThread的线程必须是此时这个对象所依附的线程【即依附线程本身才有权决定转让依附权给其他线程】
关于QThread对象的管理线程与所依附线程关系:
QThread对象的管理线程与所依附的线程不是一个线程;QThread对象管理的线程是一个新的底层线程,该线程被QThread对象管理【比如在QThread对象生命周期结束时,必须等待期管理的线程先结束】;
而QThread对象所依附的线程,是定义(创建)QThread对象的线程,可能是GUI线程也可能是其他线程;
connect链接类型参数
Qt::AutoConnection 如果发送信号所在的线程与接受者所依附的线程是同一个线程就是Qt::DirectConnection策略;否则就是Qt::QueuedConnection策略;注【这里所说的发送信号所在的线程是指触发调用 emit 信号的执行线程,并不一定是发送者所依附的线程!】
Qt::DirectConnection 同一线程情况下才会触发此命令;直接立即在同一线程内调用槽函数代码段;发送端此时会被阻塞等待立即调用的完成;原理:最简单的理解成把一段代码“临时插入”到了运行栈;【需要注意可重入性问题】
【注:若信号调用线程与接受者依附线程是不同的线程,但是connect链接强制指定了direct模式,槽函数的执行线程依然是在信号调用线程上,这意味着信号调用的地方会等待槽函数执行结束返回;如果非要谈此时接收者所依附的线程本身处于什么状态,我只能说处于处理事件循环,或者阻塞待处理事件循环的状态】
Qt::QueuedConnection 发送端与接受者所属线程不一样;存放事件到接收者所依附的线程,发送端不阻塞,继续往下执行;接收者等待所属线程的事件循环处理到此派发任务;【若发送端和接受者依附线程一样,强制使用Qt::QueuedConnection方式连接=>这其实是一种延迟行为信号发送线程发送完后继续往下执行,这时槽函数还没被执行,一直到调用信号发送的位置执行完后进入事件循环,处理到刚刚加入的事件后才执行槽函数(处理需要延迟的任务时候用)】
Qt::BlockingQueuedConnection 发送端与接受者所属线程不一样;存放事件到接收者所依附的线程,发送端阻塞等待接收者获得分派的事件任务处理完成后再执行;如果发送端线程与接受者所属线程一样;势必造成死锁行为;
Qt::UniqueConnection 独占链接;多个相同链接调用只成功一个;【相同判定:发送者-信号,接受者-槽都对应相同】
Qt::SingleShotConnection 一次性链接,触发一次槽调用后,这段链接会自动断开;
关于信号槽同一个信号链接多个槽函数的执行顺序的新标准(qt5.0之后):
所有这些链接被触发时的最终判定【即根据发送信号所在线程,接收者依附线程,以及链接策略;判定应该在哪个线程上执行槽函数】的结果;被分配在同一个线程上执行的槽函数之间的执行顺序与其connect链接的声明顺序一致;分配在不同线程上执行的槽函数之间执行的顺序不确定!
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mythread.h"
#include<QThreadPool>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);ui->widget_2->setWindowTitle("222");ui->widget_2->show();//无效ui->textEdit->setText("112");//贯穿widget容器// 1. 创建任务对象Generate* gen = new Generate(this);BubbleSort* bubble = new BubbleSort(this);QuickSort* quick = new QuickSort(this);//设置线程池线程数量QThreadPool::globalInstance()->setMaxThreadCount(3);connect(this, &MainWindow::starting, gen, &Generate::recvNum);// 2. 启动子线程//ui->start的clicked信号是GUI线程调用的,this依附的线程也是GUI线程,所以 emit starting调用是在GUI线程执行的connect(ui->start, &QPushButton::clicked, this, [=](){emit starting(10000);//因为这个是在GUI线程执行,而gen的所依附线程也是GUI线程,所以这里是在GUI线程直接调用&Generate::recvNum,再调用下面的,故这里也不会出现数据竞争QThreadPool::globalInstance()->start(gen);//将gen放入任务队列,待空闲线程取用});//一个信号链接多个槽,&Generate::sendArray的调用肯定是在另一个线程,而bubble,quick,this对象依附线程是GUI线程,所以这里三个槽函数是会在同一个线程内触发,qt新标准规定这种触发顺序与connect顺序一致connect(gen, &Generate::sendArray, bubble, &BubbleSort::recvArray);connect(gen, &Generate::sendArray, quick, &QuickSort::recvArray);// 接收子线程发送的数据connect(gen, &Generate::sendArray, this, [=](QVector<int> list){//所以这里上面的recvArray已经触发,甚至是在同一个GUI线程中触发完毕的,这里也不会有数据竞争QThreadPool::globalInstance()->start(bubble);QThreadPool::globalInstance()->start(quick);for(int i=0; i<list.size(); ++i){ui->randList->addItem(QString::number(list.at(i)));}});connect(bubble, &BubbleSort::finish, this, [=](QVector<int> list){for(int i=0; i<list.size(); ++i){ui->bubbleList->addItem(QString::number(list.at(i)));}});connect(quick, &QuickSort::finish, this, [=](QVector<int> list){for(int i=0; i<list.size(); ++i){ui->quickList->addItem(QString::number(list.at(i)));}});//因为现在gen对象其实是一个task对象而非线程对象;所以gen不需要管理线程,线程由线程池管理;//并且 task任务对象设置了setAutoDelete(true);这会在每个任务对象的run方法执行完后自动的去释放task对象;所以也不需要手动delete
// connect(this, &MainWindow::destroy, this, [=]()
// {
// gen->quit();
// gen->wait();
// gen->deleteLater(); // 等价与 delete gen;// bubble->quit();
// bubble->wait();
// bubble->deleteLater();// quick->quit();
// quick->wait();
// quick->deleteLater();
// });
}MainWindow::~MainWindow()
{delete ui;
}
相关文章:
深度解析qt核心机制:信号槽的多线程行为与对象的线程依附性
对象的线程依附性 每一个学过C以及系统编程的程序员,对于变量会与特定线程有关联都会感到不可思议;在qt中所说的对象的线程依附性,只是针对继承自QObject的对象而言的;对象的线程依附性,并不是代表真的某个底层线程才…...

关于时间格式yyyy-M-d或yyyy-MM-d到yyyy-MM-dd的转换
工作时遇到前端传的时间格式是"2023-12-3 17:41:52",和"2023-1-1 17:41:52"但是我想要的是"2023-12-03 17:41:52"和"2023-01-01 17:41:52"。下面给大家分享几个解决方法 方法一: 找前端!让他改&…...

【Windows】之微软输入法配置小鹤双拼
前言 Windows 自带的输入法微软输入法本身就是个最简洁、最方便的输入法,不需要去安装多余的第三方输入法软件。同时,微软中文拼音输入法支持双拼输入法,但微软自带的双拼输入法不包含小鹤双拼方案的。所以,在这里将会讲解如何配置…...

【AI】使用Jan.ai在本地部署大模型开启AI对话(含通过huggingface下载大模型,实现大模型自由)
文章目录 前言一、Jan.ai是什么?二、下载大模型1. 找到大模型文件地址2. 下载大模型3. 修改model.json文件 三、使用Jan调用大模型进行对话总结 前言 2023年是AIGC元年。以后,每个人多少都会接触到GPT带来的变化。别人都在用,我们也不能落下…...

C++摸版(初阶)----函数模版与类模版
本专栏内容为:C学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C 🚚代码仓库:小小unicorn的代码仓库&…...

Embedded-Project项目介绍
Embedded-Project项目介绍 Server后端项目后端启动连接数据库启动时可能遇到的问题架构介绍 web前端项目前端启动启动时可能遇到的问题架构介绍 前后端分离开发流程 项目地址: https://github.com/Catxiaobai/Embedded-Project Server后端项目 系统后端项目&#…...
golang 的那些花样
从 A Tour of Go 可以看到一些 Go 比较特殊的点 文章目录 变量声明时,类型放在后面Array 的引用 Slicereceiver 和 argumentbuilt-int特殊接口Error 变量声明时,类型放在后面 var i, j int 1, 2declaration-syntax Array 的引用 Slice slices-intro …...

如何设计企业级业务流程?学习华为的流程六级分类经验
业务流程管理(BPM)是一种系统化的方法,用于分析、设计、执行、监控和优化组织的业务流程,以实现预期的目标和价值。业务流程管理中,流程的分级方法有多种,常见的有以下几种: APQC的流程分级方法…...

视频智能分析支持摄像头异常位移检测,监测摄像机异常位移变化,保障监控状态
我们经常在生产场景中会遇到摄像头经过风吹日晒,或者异常的触碰,导致了角度或者位置的变化,这种情况下,如果不及时做出调整,会导致原本的监控条件被破坏,发生事件需要追溯的时候,查不到对应位置…...

C++ UTF-8与GBK字符的转换 —基于Linux 虚拟机 (iconv_open iconv)
1、UTF-8 和 GBK 的区别 GBK:通常简称 GB (“国标”汉语拼音首字母),GBK 包含全部中文字符。 UTF-8 :是一种国际化的编码方式,包含了世界上大部分的语种文字(简体中文字、繁体中文字、英文、…...

云原生十二问
一、什么是云原生? 云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。现代企业希望构建高度可扩展、灵活且具有弹性的应用程序,可以快速更新以满足客户需求。为此,他们使用现代工具和技术,这些工具和技术本质上支…...

K8Spod组件
一个pod能包含几个容器 一个pause容器(基础容器/父容器/根容器) 一个或者多个应用容器(业务容器) 通常一个Pod最好只包含一个应用容器,一个应用容器最好也只运行一个业务进程。 同一个Pod里的容器都是运行在同一个node节点上的,并且共享 net、…...
clickhouse-client INSERT CSV/TSV时跳过错误行
clickhouse-client INSERT CSV/TSV时跳过错误行 在使用clickhouse-client向ck中导入csv文件时,当csv中有个别行数据格式错误时,整个文件就插入失败了,经常会导致丢数据。 经过一番搜索,发现ck提供了两个参数可以跳过错误行&#x…...

直流稳压电源电路
一、稳压电源的技术指标及对稳压电源的要求 稳压电源的技术指标可以分为两大类:一类是特性指标,如输出电压、输出电滤及电压调节范围;另一类是质量指标,反映一个稳压电源的优劣,包括稳定度、等效内阻(输出电阻&#x…...
记录爬虫编写步骤
本文讲解 Python 爬虫实战案例:抓取百度贴吧(https://tieba.baidu.com/)页面,比如 Python爬虫吧、编程吧,只抓取贴吧的前 5 个页面即可。今天一个毕业学生问到一个问题:不清楚编写爬虫的步骤,不…...

SpringBoot配置Swagger2与Swagger3
swagger是什么? 在平时开发中,一个好的API文档可以减少大量的沟通成本,还可以帮助新加入项目的同事快速上手业务。大家都知道平时开发时,接口变化总是很多,有了变化就要去维护,也是一件比较头大的事情。尤…...
C/C++ 枚举
目录 枚举概述 枚举的使用 枚举的大小计算 枚举的优点 C语言中的自定义类型有:结构 位段 枚举 联合 枚举概述 枚举顾名思义就是一一列举,把可能的取值一一列举。 比如我们现实生活中:一周的星期一到星期日是有限的7天,…...

P12 音视频复合流——TS流讲解
前言 从本章开始我们将要学习嵌入式音视频的学习了 ,使用的瑞芯微的开发板 🎬 个人主页:ChenPi 🐻推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ 🔥 推荐专栏2: 《Linux C应用编程(概念类)_C…...
三维重建 3D Gaussian Splatting:实时的神经场渲染
目录 概念理解三维高斯喷洒 渲染实例 依赖项: 编译报错: 预训练模型 13G:...

Django Web框架
1、创建PyCharm项目 2、安装框架 pip install django4.2.0 3、查看安装的包列表 4、使用命令创建django项目 django-admin startproject web 5、目录结构 6、运行 cd web python manage.py runserver7、初始化后台登录的用户名密码 执行数据库迁移生成数据表 python man…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...