由Qt::BlockingQueuedConnection引起的关闭Qt主页面而后台仍有进程残留

BUG:由Qt::BlockingQueuedConnection引起的关闭Qt主页面而后台仍有进程残留

1、错误代码示例

首先我们看下下面的代码,可以思考一下代码的错误之处

/** BlockingQueueDeadLock.h **/
#pragma once#include <QtWidgets/QMainWindow>
#include "ui_BlockingQueueDeadLock.h"
#include <thread>class BlockingQueueDeadLock : public QMainWindow
{Q_OBJECTpublic:BlockingQueueDeadLock(QWidget *parent = nullptr);~BlockingQueueDeadLock();public slots:void RecvBlockingQueueSignal();
signals:void SendBlockingQueueSignal();private:void startLoopTest();void stopLoopTest();void RunInThread();private:Ui::BlockingQueueDeadLockClass ui;std::thread loopTest;bool m_StopFlag;
};/** BlockingQueueDeadLock.cpp **/
#include "BlockingQueueDeadLock.h"
#include <qdebug.h>BlockingQueueDeadLock::BlockingQueueDeadLock(QWidget *parent): QMainWindow(parent), m_StopFlag(false)
{ui.setupUi(this);startLoopTest();connect(this, &BlockingQueueDeadLock::SendBlockingQueueSignal,this, &BlockingQueueDeadLock::RecvBlockingQueueSignal, Qt::BlockingQueuedConnection);
}BlockingQueueDeadLock::~BlockingQueueDeadLock()
{stopLoopTest();
}void BlockingQueueDeadLock::RunInThread()
{qDebug("%1", std::this_thread::get_id());while (!m_StopFlag) {std::this_thread::sleep_for(std::chrono::microseconds(10));qDebug("signal thread: %1", std::this_thread::get_id());emit SendBlockingQueueSignal();}
}void BlockingQueueDeadLock::RecvBlockingQueueSignal()
{qDebug("slot thread: %1", std::this_thread::get_id());
}void BlockingQueueDeadLock::startLoopTest()
{m_StopFlag = false;loopTest = std::thread(&BlockingQueueDeadLock::RunInThread, this);
}void BlockingQueueDeadLock::stopLoopTest()
{m_StopFlag = true;if (loopTest.joinable()) {loopTest.join();}
}

上面短短几十行代码竟会导致当我关闭Qt主页面时,后台的进程并没有完全退出。
在这里插入图片描述

2、原因分析

先使用转储工具获取当前后台进程的堆栈信息;右击后台进程->创建转储文件
在这里插入图片描述

这时会获得一个DMP文件,通过windbg分析该DMP文件,如下图所示
在这里插入图片描述

我们可以清晰的看到后台进程一直在等待join函数的退出;阅读源码分析join最终调用的时_Thrd_join这个接口,该接口是阻塞的,需要等待线程的主函数运行结束后才会返回。

也就是说我们RunInThread线程主函数迟迟没有结束。

BlockingQueueDeadLock::~BlockingQueueDeadLock()
{stopLoopTest();
}void BlockingQueueDeadLock::RunInThread()
{qDebug("%1", std::this_thread::get_id());while (!m_StopFlag) {std::this_thread::sleep_for(std::chrono::microseconds(10));qDebug("signal thread: %1", std::this_thread::get_id());emit SendBlockingQueueSignal();}
}
void BlockingQueueDeadLock::stopLoopTest()
{m_StopFlag = true;if (loopTest.joinable()) {loopTest.join();}
}

线程的主函数就是一个while循环,在我们BlockingQueueDeadLock析构的时候会将标识符m_StopFlag置为true;按道理讲该while循环应该很快的结束并返回。

但是!但是!但是!我们是否忽略了emit这个信号发射的是如何connect的呢?

connect(this, &BlockingQueueDeadLock::SendBlockingQueueSignal,this, &BlockingQueueDeadLock::RecvBlockingQueueSignal, Qt::BlockingQueuedConnection);

非常非常奇怪的是为什么connect最后一个参数是Qt::BlockingQueuedConnection呢?我看到这个代码也会很奇怪,可能之前的开发者认为发送信号和接受信号的slot不在同一个线程吧!!

3、解决方案

奇怪的地方必有妖,没错就是Qt::BlockingQueuedConnection这里有问题。

先看Qt官方文档的解释:
在这里插入图片描述

非常明确的指出了发射的信号与接收的槽不能在同一个线程里,否者会导致应用死锁。

想要了解Qt BlockingQueuedConnection源码的同学可以看下面的文章:

14.QueuedConnection和BlockingQueuedConnection连接方式源码分析_Master Cui的博客-CSDN博客
在这里插入图片描述

明确的指出了使用BlockingQueuedConnection同一个线程时死锁的原因。

因此,我们只需要将Qt::BlockingQueuedConnection最后的参数去除,使用默认参数即可。

这就是我发现的Qt主界面关闭,而后台进程未释放的问题;还是由于后台进程中的某个线程没有释放,而该线程没有结束又是由于Qt::BlockingQueuedConnection死锁导致的。

4、总结

当前Qt主界面关闭,而后台进程未释放的原因有很多。这里只是展示了其中一个原因:后台进程中有线程未及时释放导致的,也为遇到同样问题的你提供一个思路。

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

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

相关文章

Android 13.0 Launcher3定制之双层改单层(去掉抽屉式四)

1.概述 在13.0的系统产品开发中,对于在Launcher3中的抽屉模式中,系统默认的就是抽屉单层模式,但是在很多产品中需要默认为单层模式,就是要求去掉双层抽屉模式,接下来看下如何继续实现去掉抽屉双层模式,来变成单层模式第四节 2.Launcher3定制之双层改单层(去掉抽屉式四)的…

初阶扫雷(超详解)

✨博客主页&#xff1a;小钱编程成长记 &#x1f388;博客专栏&#xff1a;C语言小游戏 &#x1f388;推荐相关博文&#xff1a;初阶三子棋&#xff08;超详解&#xff09; 初阶扫雷 1.游戏介绍2.基本思路3.实现前的准备4.实现步骤4.1 打印菜单4.2 初始化扫雷棋盘4.3 打印扫雷棋…

单片机之硬件记录

一、概念 VBAT 当使用电池或其他电源连接到VBAT脚上时&#xff0c;当VDD断电时&#xff0c;可以保存备份寄存器的内容和维持RTC的功能。如果应用中没有使用外部电池&#xff0c;VBAT引脚应接到VDD引脚上。 VCC&#xff1a;Ccircuit 表示电路的意思,即接入电路的电压&#x…

C语言入门Day_18 判断和循坏的小结

目录 前言&#xff1a; 1.判断 2.循环 3.课堂笔记 4.思维导图 前言&#xff1a; 判断语句和循环语句都可以大致分为三个部分&#xff0c;第一个部分是固定的语法格式&#xff1b;第二部分是代码的执行顺序&#xff0c;第三部分是判断和循环成立与否的判断条件。 1.判断 1…

Allegro166版本如何在颜色管理器中实时显示层面操作指导

Allegro166版本如何在颜色管理器中实时显示层面操作指导 在用Allegro166进行PCB设计的时候,需要在颜色管理器中频繁的开关层面。但是166不像172一样在颜色管理器中可以实时的开关层面,如下图 需要打开Board Geometry/Soldermask_top层,首先需要勾选这个层面,再点击Apply即…

ubuntu 20.04 docker 安装 mysql

要在Ubuntu 20.04上安装Docker并运行MySQL容器&#xff0c;您可以按照以下步骤操作&#xff1a; 1.更新系统包列表&#xff1a; sudo apt update2.安装Docker&#xff1a; sudo apt install docker.io3.启动Docker服务并设置其开机自启动&#xff1a; sudo systemctl start…

直播|DITA内容发布工具解析 - 问答总结

9月6日&#xff0c;我们进行了一场名为“DITA内容发布工具解析”的直播。通过直播&#xff0c;大家了解到&#xff1a; DITA-OT简介 默认输出效果 定制以后输出效果 发布过程与样式定制 在问答环节&#xff0c;大家进行了热烈沟通。我将几个大家关心的问题和答复总结如下&…

Vue2进阶篇学习笔记

文章目录 Vue2进阶学习笔记前言1、Vue脚手架学习1.1 Vue脚手架概述1.2 Vue脚手架安装1.3 常用属性1.4 插件 2、组件基本概述3、非单文件组件3.1 非单文件组件的基本使用3.2 组件的嵌套 4、单文件组件4.1 快速体验4.2 Todo案例 5、浏览器本地存储6、组件的自定义事件6.1 使用自定…

k8s集群calio网络问题

k8s calio节点报错 Readiness probe failed: calico/node is not ready: BIRD is not ready: BGP not established with 172.24.0.12023-09-07 05:42:37.176 [INFO][200] health.go 156: Number of node(s) with BGP peering established 0这个错误是由于不同主机网卡不一致造…

【Image captioning】Dual-Level Collaborative Transformer for Image Captioning在自定义数据集的调试与实现(过程完整详细)

Dual-Level Collaborative Transformer for Image Captioning在自定义数据集的调试与实现(过程完整详细) 作者:安静到无声 个人主页 目录 Dual-Level Collaborative Transformer for Image Captioning在自定义数据集的调试与实现(过程完整详细)环境配置生成 region featu…

MySQL 全局锁、表级锁、行锁详解

前言 MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类&#xff0c;全局锁和表级锁是在server层实现的。 全局锁 全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法&#xff0c;命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状…

beetlsql3.x版本适配达梦数据库

BeetlSQL介绍 BeetlSQL的目标是提供开发高效&#xff0c;维护高效&#xff0c;运行高效的数据库访问框架&#xff0c;在一个系统多个库的情况下&#xff0c;提供一致的编写代码方式。支持如下数据平台 传统数据库&#xff1a;MySQL(国内兼容MySQL协议的各种大数据库),MariaDB…

软件测试/测试开发丨测试用例自动录入 学习笔记

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27139 测试用例自动录入 测试用例自动录入的价值 省略人工同步的步骤&#xff0c;节省时间 兼容代码版本的自动化测试用例 用例的执行与调度统一化管理…

新时代的监控系统--网站可观测性的基础功能

观测云RUM基础功能 更快地故障调试速度 理解用户体验&#xff0c;包括应用性能数据&#xff0c;比如网站核心指标等实时识别报错的设备、系统或国家&#xff0c;快速标注错误的部位解决客户端错误&#xff0c;包括特定用户&#xff0c;包括一键定位代码错误 监控100%前端错误和…

【数据结构】红黑树的插入与验证

文章目录 一、基本概念1.时代背景2. 基本概念3.基本性质 二、实现原理1. 插入1.1变色1.2旋转变色①左旋②右旋③右左双旋④左右双旋 2.验证 源码总结 一、基本概念 1.时代背景 1972年鲁道夫拜尔(Rudolf Bayer)发明了一种数据结构&#xff0c;这是一种特殊的B树4阶情况。这些树…

go初识iris框架(四) -框架设置操作

前言 iris(1) iris(2) iris(3) 框架设置操作 当我们的一个路径是xxx/user/info,xxx/user/login,xxx/user/register的时候,我们发现前面都有一个user,我们如果用/{data:string}这样的话这样导致我们的路径是灵活的&#xff0c;所以我们得用其他方法 这里我们的路径是以anime为…

蓝桥杯打卡Day6

文章目录 N的阶乘基本算术整数查询 一、N的阶乘OI链接 本题思路&#xff1a;本题是关于高精度的模板题。 #pragma GCC optimize(3) #include <bits/stdc.h>constexpr int N1010;std::vector<int> a; std::vector<int> f[N];std::vector<int> mul(in…

MATLAB入门-数据的导入和导出

MATLAB入门-数据的导入和导出 注&#xff1a;本篇文章是课程学习笔记&#xff0c;课程链接为&#xff1a;头歌 常见的几个导入数据的方法 load函数 load函数专门用于引入MATLAB的.mat格式数据&#xff0c;十分的简单方便。 例如&#xff1a;一个-ASCII编码形式存储的数据文件…

使用本地mysql+linux实现mysql主从同步

1.配置linux 保证linux已经安装好了mysql1.1修改该linux配置文件 vim /etc/my.cnf1.2重启linux的mysql systemctl restart mysqld1.3使用账户密码登录linux中的mysql,查看是否配置成功 mysql> show master status;若显示有FIile和Posttion就表示注linux的主节点配置成功…

【算法】一文带你从浅至深入门dp动态规划

文章目录 一、前言二、动态规划理论基础1、基本概念2、动态规划五部曲【✔】3、出错了如何排查&#xff1f; 三、实战演练&#x1f5e1;0x00 斐波那契数0x01 第N个泰波那契数0x02 爬楼梯0x03 三步问题0x04 使用最小花费爬楼梯⭐解法一解法二 0x05 解码方法* 四、总结与提炼 一、…