由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.hqwc.cn/news/101417.html

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

相关文章

【C刷题训练营】第三讲(c语言入门训练)

前言: 大家好&#xff0c;我决定日后逐渐更新c刷题训练营的内容&#xff0c;或许能帮到入门c语言的初学者&#xff0c;如果文章有错误&#xff0c;非常欢迎你的指正&#xff01; &#x1f4a5;&#x1f388;个人主页:​​​​​​Dream_Chaser&#xff5e; &#x1f388;&…

Linux命令200例:Yum强大的包管理工具使用(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…

MindsDB为许多不支持内置机器学习的数据库带来了机器学习功能

选择平台的首要原则是“靠近数据”,让代码靠近数据是保持低延迟的必要条件。 机器学习,特别是深度学习往往会多次遍历所有数据(遍历一次被称为一个epoch)。对于非常大的数据集来说,理想的情况是在存储数据的地方建立模型,这样就不需要大量的数据传输。目前已经有部分数据…

不用额外插件?RunnerGo内置压测模式怎么选

我们在做性能测试时需要根据性能需求配置不同的压测模式如&#xff1a;阶梯模式。使用jmeter时我们需要安装插件来配置测试模式&#xff0c;为了方便用户使用&#xff0c;RunnerGo内嵌了压测模式这一选项&#xff0c;今天给大家介绍一下RunnerGo的几种压测模式和怎么根据性能需…

手写Spring:第19章-JDBC功能整合

文章目录 一、目标&#xff1a;JDBC功能整合二、设计&#xff1a;JDBC功能整合三、实现&#xff1a;JDBC功能整合3.1 工程结构3.2 整合JDBC功能核心类图3.3 数据源操作3.3.1 数据源操作抽象类3.3.2 JDBC 工具类 3.4 数据库执行3.4.1 语句处理器接口3.4.2 结果处理器接口3.4.3 行…

你参与的APP开发项目安全吗?

Android将安全设计贯穿系统架构的各个层面&#xff0c;覆盖系统内核、虚拟机、应用程序框架层以及应用层各个环节&#xff0c;力求在开放的同时&#xff0c;也恰当保护用户的数据、应用程序和设备的安全。Android安全模型主要提供以下几种安全机制&#xff1a; 进程沙箱隔离机…

slog正式版来了:Go日志记录新选择!

在大约一年前&#xff0c;我就写下了《slog&#xff1a;Go官方版结构化日志包[1]》一文&#xff0c;文中介绍了Go团队正在设计并计划在下一个Go版本中落地的Go官方结构化日志包&#xff1a;slog[2]。但slog并未如预期在Go 1.20版本[3]中落地&#xff0c;而是在golang.org/x/exp…

复制粘贴是怎么实现的

在上面的代码中&#xff0c;command 和 select 是自定义的函数。它们的作用如下&#xff1a; 实现复制粘贴的思路&#xff1a; 创建一个 textarea 标签将 textarea 移出可视区域给这个 textarea 赋值将这个 textarea 标签添加到页面中调用 textarea 的 select 方法调用 docum…

曾国藩农民出身,弯道超车实现逆袭的大智慧

曾国藩从小就笨笨的&#xff0c;读书多了才开窍&#xff0c;实现人生逆袭。农民出身&#xff0c;弯道超车&#xff0c;贵在坚持。 约翰生说过&#xff1a;“成大事不在于力量的大小&#xff0c;而在于能坚持多久。” 很多家长认为“不让孩子输在起跑线上”&#xff0c;这是错…

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书杭州师范大学图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书杭州师范大学图书馆

壁炉在文学和艺术中的代表着什么呢,能给我们带来什么样的影响?

壁炉&#xff0c;不仅仅是家庭温暖的来源&#xff0c;也是文学和艺术中常见的重要元素。它的形象在文学作品、绘画和电影中频繁出现&#xff0c;不仅为故事情节提供了背景&#xff0c;还象征着情感、温馨和安全感。让我们一起深入探讨壁炉在文学和艺术中的形象&#xff0c;以及…

U-net网络学习记录

U-net网络 本质上是一个用于图像分割的神经网络 输入是一幅图&#xff0c;输出是目标的分割结果。继续简化就是&#xff0c;一幅图&#xff0c;编码&#xff0c;或者说降采样&#xff0c;然后解码&#xff0c;也就是升采样&#xff0c;然后输出一个分割结果。根据结果和真实分…