Qt 信号和槽执行线程问题

Qt 信号和槽执行线程问题

1.QObject的thread() 函数

在Qt中QObject有一个thread() 的函数。这就说明,每一个QOject对象都有所属的线程。

注意:

  • QThread也是一个QObject的子类,也有自己所在的线程
  • 每个QObject存在父子关系,存在父子关系的两个对象必须在同一个线程中

对于信号和槽的连接,这里涉及到几个对象的thread:

  • sender()
  • receiver()
  • emit 线程

2.信号和槽的连接方式

1. Qt::AutoConnection

Qt::AutoConnection是默认连接类型。如果信号接收方与发送方在同一个线程,它将使用Qt::DirectConnection,否则使用Qt::QueuedConnection。连接类型在信号发射时决定。

这时细心的你 好奇这里连接类型确定为啥不是在连接的时候确定而是在信号发射的时候确定的呢?

2. Qt::DirectConnection

Qt::DirectConnection将导致信号所连接的槽函数立即在发射信号的线程中执行。这意味着槽函数的执行与信号的发射是同步的。如果槽函数执行耗时操作或信号由UI线程发射,可能会导致UI无响应。

3. Qt::QueuedConnection

Qt::QueuedConnection将导致槽函数在接收者所在的线程中执行。这种连接方式下,如果信号被多次触发,相应的槽函数会按照顺序在接收者线程中依次执行。注意:使用QueuedConnection时,参数类型必须是Qt基本类型,或者使用qRegisterMetaType()进行注册的自定义类型。

这也就是对我们说,如果槽函数想在另一个线程执行,把接受者的thread改到另一个线程即可, moveToThread()函数可以做到。

4.Qt::BlockingQueuedConnection

Qt::BlockingQueuedConnection与Qt::QueuedConnection类似,区别在于发送信号的线程在槽函数执行完毕之前会一直处于阻塞状态。因此,发送方和接收方必须处于不同的线程,否则可能导致死锁。

5.Qt::UniqueConnection

Qt::UniqueConnection可以与以上所有连接类型搭配使用。一旦设置了Qt::UniqueConnection,同一信号与同一槽函数的二次连接将会失败,确保了连接的唯一性。

3.信号和槽的执行

(1) 信号执行所在的线程

这个毫无疑问,这个调用emit信号函数的线程。对,信号也就是个函数。

(2)发送者的thread()

这个就是发送者的对象所依附的线程

(3)槽函数执行所造的线程

这个是根据上方连接方式确定的

(4)接受者的thread()

这个就是发送者的对象所依附的线程

显然,我们对于(1)(2)(4)的线程我们很容易确定。唯一不能确定的是槽函数所在的线程。要测试槽函数所在的线程其实很简单,打印线程id就可以了,关键是要想清楚上述几个线程即可,写个代码验证即可!

注意:信号发射的线程可以和发送者的不在一个线程

多读几次上面加粗部分的提示,相信你也可以想明白信号和槽执行的线程问题!

4.简单原理简介

主要原理这里简单介绍一下,下次会有更详细的源码分析。

直连和唯一连接很好实现,主要是队列连接和阻塞连接不好理解。

这里要了解一下QThread和QEventLoop。 QThread中run()的默认实现调用了exec(),从而创建一个QEventLoop对象,由QEventLoop对象处理线程中事件队列(每一个线程都有一个属于自己的事件队列,并且线程安全)中的事件。exec()在其内部不断做着循环遍历事件队列的工作。 QThread默认start是开启一个线程,调用run() 函数,进入事件循环,然后将对象移动到到其他线程后。发射信号,则根据连接类型,进行不同的处理。队列连接是往接受者的线程post一个事件,阻塞连接则是send发送一个事件。(Qt源码中是采用QMetaCallEvent事件间接去触发的,以避免子线程未启动的情况。)

(1)以下是源码:

/*!The starting point for the thread. After calling start(), thenewly created thread calls this function. The defaultimplementation simply calls exec().You can reimplement this function to facilitate advanced threadmanagement. Returning from this method will end the execution ofthe thread.\sa start(), wait()
*/
void QThread::run()
{(void) exec();
}/*!Enters the event loop and waits until exit() is called, returning the valuethat was passed to exit(). The value returned is 0 if exit() is called viaquit().This function is meant to be called from within run(). It is necessary tocall this function to start event handling.\sa quit(), exit()
*/
int QThread::exec()
{Q_D(QThread);QMutexLocker locker(&d->mutex);d->data->quitNow = false;if (d->exited) {d->exited = false;return d->returnCode;}locker.unlock();QEventLoop eventLoop;int returnCode = eventLoop.exec();locker.relock();d->exited = false;d->returnCode = -1;return returnCode;
}

(2)activate函数对于线程的判断

 	Qt::HANDLE currentThreadId = QThread::currentThreadId(); // 发送线程idbool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.loadRelaxed(); // 发送者线程id// We need to check against the highest connection id to ensure that signals added// during the signal emission are not emitted in this emission.uint highestConnectionId = connections->currentConnectionId.loadRelaxed();do {QObjectPrivate::Connection *c = list->first.loadRelaxed();if (!c)continue;do {QObject * const receiver = c->receiver.loadRelaxed();if (!receiver)continue;QThreadData *td = c->receiverThreadData.loadRelaxed(); // 接收者线程idif (!td)continue;bool receiverInSameThread;if (inSenderThread) {receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();} else {// need to lock before reading the threadId, because moveToThread() could interfereQMutexLocker lock(signalSlotLock(receiver));receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();}}while(..);}while(..);

(3)执行槽函数判断

        // determine if this connection should be sent immediately or// put into the event queueif ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)|| (c->connectionType == Qt::QueuedConnection)) {queued_activate(sender, signal_index, c, argv);continue;
#if QT_CONFIG(thread)} else if (c->connectionType == Qt::BlockingQueuedConnection) {if (receiverInSameThread) {qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: ""Sender is %s(%p), receiver is %s(%p)",sender->metaObject()->className(), sender,receiver->metaObject()->className(), receiver);}QSemaphore semaphore;{QBasicMutexLocker locker(signalSlotLock(sender));if (!c->receiver.loadAcquire())continue;QMetaCallEvent *ev = c->isSlotObject ?new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) :new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction,sender, signal_index, argv, &semaphore);QCoreApplication::postEvent(receiver, ev);}semaphore.acquire();continue;
#endif}

5.结论

  • 槽函数执行的线程根据接收者所在线程和连接方式
  • 对象的所依附的线程取决于父对象和所创建的线程

注意:

  • 要moveToThread时候千万不要指定父对象,否则移动不过去
  • 移动到的线程必须start才能真正在其线程执行
  • QThread本身也是QObject,指定父对象后自己的thread()不是自己

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

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

相关文章

【Java-LangChain:面向开发者的提示工程-6】文本转换

第六章 文本转换 LLM非常擅长将输入转换成不同的格式,典型应用包括多语种文本翻译、拼写及语法纠正、语气调整、格式转换等。 本章节将介绍如何使用编程的方式,调用API接口来实现“文本转换”功能。 环境配置 参考第二章的 环境配置小节内容即可。 文…

java复习回顾

文章目录 0 开发工具1. 对象和类、三大特性2. 成员/实例变量和实例变量(this关键字)3. 方法重载overload4. 构造方法和this关键字5. 继承6. 访问修饰符7. 方法重写8. 继承下的构造执行9. 多态9.1 向上转型9.2 向下转型9.3 多态的应用 0 开发工具 Maven是…

Acwing 907. 区间覆盖

Acwing 907. 区间覆盖 知识点题目描述思路讲解代码展示 知识点 贪心 题目描述 思路讲解 代码展示 #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n;struct Range {int l, r;bool operator < (const Range &W) …

分页前后端完整代码

一、后端代码 User实体类要继承PageVo package com.like.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.like.common.PageVo; import…

借助ChatGPT的神奇力量,解锁AI无限可能!

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前学习C/C、算法、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&…

计算机毕业设计 基于SSM的宿舍管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

时间序列-AR模型与MA模型的原理与实现

文章目录 1 自回归模型AR Model1.1 自回归模型 vs 多元线性回归模型1.1.1 线性回归1.1.2 AR(1)模型1.1.3 AR(p)模型 1.2 AR建模问题 2 移动平均模型 MA Model2.1 MA模型的数学表示2.1.1 MA(1)模型2.2.2 MA(q)模型 2.2 MA建模问题 ARIMA模型是AR模型&#xff08;自回归模型&…

使用 Python 给 PDF 添加目录书签

0、库的选择——pypdf 原因&#xff1a;Python Version Support Python 3.11 3.10 3.9 3.8 3.7 3.6 2.7 pypdf>3.0 YES YES YES YES YES YES PyPDF2>2.0 YES YES YES YES YES YES PyPDF2 1.20.0 - 1.28.4 YES YES YES YES YES YES P…

c语言练习74: 分割数组中数字的数位

分割数组中数字的数位 题⽬描述&#xff1a; 给你⼀个正整数数组 nums &#xff0c;请你返回⼀个数组 answer &#xff0c;你需要将 nums 中每个整数进⾏数位 分割后&#xff0c;按照 nums 中出现的 相同顺序 放⼊答案数组中。 对⼀个整数进⾏数位分割&#xff0c;指的是将整…

91、Redis - 事务 与 订阅-发布 相关的命令 及 演示

★ 事务相关的命令 Redis事务保证事务内的多条命令会按顺序作为整体执行&#xff0c;其他客户端发出的请求绝不可能被插入到事务处理的中间&#xff0c; 这样可以保证事务内所有命令作为一个隔离操作被执行。 Redis事务同样具有原子性&#xff0c;事务内所有命令要么全部被执…

2、【第一步】Qlib安装

Qlib安装 Qlib同时支持Windows和Linux。建议在Linux中使用Qlib。Qlib支持Python3,最高支持到Python3.8。 用户可以按照以下命令通过 pip 轻松安装 Qlib: pip install pyqlib同样,用户也可以按照以下步骤通过源代码安装 Qlib: 进入Qlib的根目录。然后,请执行以下命令来安装…

2024北京智慧养老展,北京养老应用软件展,北京陪护机器人展

2024第11届中国&#xff08;北京&#xff09;国际智慧养老产业展览会 The 2024 China (Beijing) international pension Industry Exhibition 时间&#xff1a;2024年04月10日—12日 展馆&#xff1a;北京亦创国际会展中心 承办&#xff1a;北京联诚国际展览有限公司 大会概要…

年度顶级赛事来袭:2023 CCF大数据与计算智能大赛首批赛题上线!

久等了&#xff01; 大数据与人工智能领域年度顶级盛事——2023 CCF大数据与计算智能大赛——首批赛题已上线&#xff0c;大赛火力全开&#xff0c;只等你来挑战&#xff01; 大赛介绍 CCF大数据与计算智能大赛&#xff08;CCF Big Data & Computing Intelligence Contes…

深入学习git

1、git原理及整体架构图 一些常用的命令 git add . 或 git add src/com/ygl/hello/hello.java 指定文件 git commit . 或 git commit src/com/ygl/hello/hello.java 指定文件 git push origin 分支名称 2、git stash的应用场景 场景一&#xff1a;你正在当前分支A开发&…

CA(openssl)

目录 创建私有CA 1、创建CA所需要的文件 2、生成CA私钥 3、生成CA自签名证书 申请证书并颁发证书 1、为需要使用证书的主机生成私钥 2、为需要使用证书的主机生成证书申请文件 3、在CA签署证书并将证书颁发给请求者 改变检查策略 4、查看证书中的信息 5、index.txt和…

多目标平衡黏菌算法(MOEOSMA)求解八个现实世界受约束的工程问题

目录 1 受约束的工程问题 1.1 减速器设计问题(Speed reducer design problem) 1.2 弹簧设计问题(Spring design problem) 1.3 静压推力轴承设计问题(Hydrostatic thrust bearing design problem) 1.4 振动平台设计问题(Vibrating platform design problem) 1.5 汽车侧面碰…

云安全【阿里云ECS攻防】

关于VPC的概念还请看&#xff1a;记录一下弹性计算云服务的一些词汇概念 - 火线 Zone-安全攻防社区 一、初始化访问 1、元数据 1.1、SSRF导致读取元数据 如果管理员给ECS配置了RAM角色&#xff0c;那么就可以获得临时凭证 如果配置RAM角色 在获取ram临时凭证的时候&#xff…

铁道货车通用技术条件

声明 本文是学习GB-T 5600-2018 铁道货车通用技术条件. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 90 mm90 mm。 B.2 制造要求 B.2.1 车体钢结构组成后&#xff1a; a) 敞车钢质侧、端板的平面度公差应小于或等于15 mm/m; 压型侧、端板的平面度…

python生成中金所期权行权价

参考沪深300股指期权的合约表&#xff0c;写一个工具函数&#xff1a; 使用方法 def get_format_option_gap(value: float, deviation: int 0): # 根据中证1000指数获取点位"""根据标准的行权价&#xff0c;生成不同档位的期权列表&#xff0c;适合中金所:…

[红明谷CTF 2021]write_shell %09绕过过滤空格 ``执行

目录 1.正常短标签 2.短标签配合内联执行 看看代码 <?php error_reporting(0); highlight_file(__FILE__); function check($input){if(preg_match("/| |_|php|;|~|\\^|\\|eval|{|}/i",$input)){ 过滤了 木马类型的东西// if(preg_match("/| |_||php/&quo…