关于Qt在子线程中使用通讯时发生无法接收数据的情况
在多线程应用中,串口通讯或TCP通讯的场景常常涉及到持续的读写操作,如果子线程处理不当,可能会导致信号阻塞问题。本文将通过串口通讯或TCP通讯为例,详细解释如何在多线程环境中避免信号阻塞,并提供代码示例。
1. 问题背景
假设我们在一个应用程序中使用多线程处理串口或TCP通讯,通常会在子线程中实现持续的数据读取。为了确保实时处理数据,常见的做法是在子线程的 run() 方法中使用 while 循环。然而,如果没有正确处理事件循环,子线程可能会出现无法接收信号或阻塞的现象。
串口或TCP通讯的基本结构
通常,串口或TCP通讯的流程如下:
- 子线程负责监听串口或TCP端口,接收数据。
- 主线程通过信号槽机制向子线程发送控制命令。
- 子线程接收到命令后执行相应操作,并将结果通过信号传回主线程。
2. 常见信号阻塞现象
在没有处理事件循环的情况下,子线程执行如下代码:
示例:阻塞信号的串口通讯
class SerialThread : public QThread {Q_OBJECT
public:SerialThread() {// 连接信号和槽connect(this, &SerialThread::dataReceived, this, &SerialThread::handleData);}signals:void dataReceived(const QByteArray &data); // 数据接收信号public slots:void handleData(const QByteArray &data) {qDebug() << "Data received in thread:" << data;}protected:void run() override {QSerialPort serial; // 假设使用Qt的QSerialPort类serial.setPortName("COM1");serial.setBaudRate(QSerialPort::Baud115200);if (!serial.open(QIODevice::ReadWrite)) {qDebug() << "Failed to open serial port!";return;}while (true) {if (serial.waitForReadyRead(1000)) { // 等待数据到来QByteArray data = serial.readAll();emit dataReceived(data); // 发出数据接收信号}}}
};
问题分析:
在上述代码中,子线程通过 while 循环不断监听串口数据,数据到达时发出 dataReceived 信号。然而,由于线程在 while 循环中没有进入事件循环,其他信号(例如来自主线程的控制命令)可能无法被处理,导致信号阻塞。
示例:阻塞信号的TCP通讯
class TcpThread : public QThread {Q_OBJECT
public:TcpThread() {connect(this, &TcpThread::messageReceived, this, &TcpThread::handleMessage);}signals:void messageReceived(const QString &message); // 接收TCP消息的信号public slots:void handleMessage(const QString &message) {qDebug() << "Message received in thread:" << message;}protected:void run() override {QTcpSocket socket;socket.connectToHost("127.0.0.1", 8080);if (!socket.waitForConnected(3000)) {qDebug() << "Failed to connect!";return;}while (true) {if (socket.waitForReadyRead(1000)) {QString message = socket.readAll();emit messageReceived(message); // 发出消息接收信号}}}
};
这里的问题与串口通讯类似,while 循环导致线程无法进入事件循环,可能会阻塞信号的处理。
3. 使用 QEventLoop 解决信号阻塞问题
为了避免信号阻塞,我们可以在 while 循环中使用 QEventLoop。这种方式确保了线程在执行任务的同时,仍然能够处理来自其他对象的信号。
示例:使用 QEventLoop 的串口通讯
class SerialThread : public QThread {Q_OBJECT
public:SerialThread() {connect(this, &SerialThread::dataReceived, this, &SerialThread::handleData);}signals:void dataReceived(const QByteArray &data);public slots:void handleData(const QByteArray &data) {qDebug() << "Data received in thread:" << data;}protected:void run() override {QSerialPort serial;serial.setPortName("COM1");serial.setBaudRate(QSerialPort::Baud115200);if (!serial.open(QIODevice::ReadWrite)) {qDebug() << "Failed to open serial port!";return;}QEventLoop eventLoop;while (true) {if (serial.waitForReadyRead(1000)) {QByteArray data = serial.readAll();emit dataReceived(data); // 发出信号}// 每次等待任务时启动局部事件循环QTimer::singleShot(10, &eventLoop, &QEventLoop::quit);eventLoop.exec(); // 进入事件循环以处理信号}}
};
示例:使用 QEventLoop 的TCP通讯
class TcpThread : public QThread {Q_OBJECT
public:TcpThread() {connect(this, &TcpThread::messageReceived, this, &TcpThread::handleMessage);}signals:void messageReceived(const QString &message);public slots:void handleMessage(const QString &message) {qDebug() << "Message received in thread:" << message;}protected:void run() override {QTcpSocket socket;socket.connectToHost("127.0.0.1", 8080);if (!socket.waitForConnected(3000)) {qDebug() << "Failed to connect!";return;}QEventLoop eventLoop;while (true) {if (socket.waitForReadyRead(1000)) {QString message = socket.readAll();emit messageReceived(message); // 发出信号}QTimer::singleShot(10, &eventLoop, &QEventLoop::quit);eventLoop.exec(); // 进入事件循环以处理信号}}
};
4. 总结
通过上述示例可以看出,在 Qt 的多线程通讯场景下,使用 while 循环容易导致信号的阻塞。引入局部事件循环(QEventLoop)可以有效解决这一问题,确保线程既能执行持续的任务,也能及时响应来自其他对象的信号。
使用局部事件循环的好处:
- 保持线程内任务的执行不被打断。
- 确保信号槽机制正常工作,信号不会被阻塞。
- 提升程序的响应性,特别是在处理通讯时尤为重要。
相关文章:
关于Qt在子线程中使用通讯时发生无法接收数据的情况
在多线程应用中,串口通讯或TCP通讯的场景常常涉及到持续的读写操作,如果子线程处理不当,可能会导致信号阻塞问题。本文将通过串口通讯或TCP通讯为例,详细解释如何在多线程环境中避免信号阻塞,并提供代码示例。 1. 问题…...
HTML:从历史演进到未来创新的网页基石
该论文为AI生成,请勿运用到正式的论文上,以下仅供参考 一、引言 1.1 研究背景 HTML(Hypertext Markup Language)作为网页构建的基础语言,在互联网的发展历程中占据着至关重要的地位。自 1993 年诞生以来,…...
向量的叉积、点积、外积
向量的叉积、点积和外积是向量代数中非常重要的操作,用于描述向量间的关系。它们广泛应用于物理、计算机图形学、几何以及蛋白质结构分析等领域。下面对每个运算进行详细介绍,并通过 PyTorch 示例代码展示其实现。 1. 点积 (Dot Product) 点积是两个向量之间的数量积,结果…...
UNI-APP 溢出隐藏显示省略号
✍经常在项目里面使用到,又没有记住懒得找了,故此写一篇记录一下! CSS单行显示省略号 /* CSS样式 */ .ellipsis {overflow: hidden; /* 隐藏超出的内容 */text-overflow: ellipsis; /* 显示省略号 */white-space: nowrap; /* 不换行 */ } CS…...
SAP学习笔记 - 开发03 - CDSView开发环境搭建,Eclipse中连接SAP,CDSView创建
上一章讲了BTP的账号创建,环境搭建等内容。 SAP学习笔记 - 开发02 - BTP实操流程(账号注册,BTP控制台,BTP集成开发环境搭建)-CSDN博客 本章继续讲SAP开发。 - CDSView 的开发环境(Eclipse)搭建…...
uniapp写的一个年月日时分秒时间选择功能
代码: <template><view><picker mode"multiSelector" :value"multiIndex" :range"multiRange" change"onMultiChange"><view class"picker">当前选择:{{ formattedDateTime }}</vie…...
golang hertz框架入门
两种模式新建项目 1、手动新建项目 2、使用hz工具新建项目 一、手动创建项目,并拉取框架 1、新建项目目录 hertz_demo_w 2、在项目跟目录新建main.go 文件 package mainimport ("context""github.com/cloudwego/hertz/pkg/app""github.…...
Android Home应用程序启动流程
Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了,本文将详细分析Launcher应用程序的启…...
C++笔试强训12、13、14
文章目录 笔试强训12一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训13一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训14一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训12 一、选择题 1-5题 引用:是一个别名,与其被引用的实…...
Excel和Word日常使用记录:
Excel使用总结 表格颜色填充: 合并单元格: 选中你要合并的单元格区域。 按下快捷键 Alt H,然后松开这些键。 再按下 M,接着按 C。 这个组合键执行的操作是:Alt H:打开“主页”选项卡。 M:选…...
用Git把本地仓库上传到远程仓库
用Git把本地仓库上传到远程仓库 GitHub创建远程仓库 在创建新仓库界面里输入你的仓库名后点击Create repository就好了。 创建本地项目 当你已经有一个项目后在命令行输入如下指令即可 git init git commit -m "first commit" git branch -M main git remote a…...
OneHotEncoder一个不太合理的地方
OneHotEncoder,在Xtrain上fit,在Xtest上transform 如果遇到某个值出现在Xtest,而没有在Xtrain出现过时,会抛出如下错误: OneHotEncoder Found unknown categories [xxx] in column xx during transform OneHotEncoder …...
如何修复软件中的BUG
笔者上一篇博文《如何开发出一款优秀的软件》主要讲了如何开发一款优秀的软件及相应的必要条件。但对一个已上线,已经成型的产品,该如何解决存在的bug呢?这是本文要阐述的内容。 在这里,首先说一下bug的种类及bug严重程度分类&…...
分享一个基于微信小程序的医院挂号就诊一体化平台uniapp医院辅助挂号应用小程序设计(源码、调试、LW、开题、PPT)
💕💕作者:计算机源码社 💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流&…...
HTML生日蛋糕
目录 写在前面 完整代码 代码分析 系列文章 写在最后 写在前面 HTML实现的生日蛋糕来喽,小编亲测,发给好友可以直接打开哦。在代码的第183行可以写下对朋友的祝福,快拿去送给你的好朋友吧! 完整代码 <!DOCTYPE html>…...
【软件逆向】第27课,软件逆向安全工程师之(二)寄存器寻址,每天5分钟学习逆向吧!
寄存器寻址是汇编语言中的一种寻址方式,在这种方式中,操作数位于CPU的寄存器中。寄存器是CPU内部的高速存储位置,用于快速访问数据。以下是关于寄存器寻址的详细信息: 寄存器寻址的特点: 操作数在寄存器中࿱…...
前缀和 — 利用前缀信息解决子数组问题
【前缀和的核心思想是预先处理数组来快速计算任意子数组的和,基本上用于数组和序列问题。】 前缀和算法具体步骤 构造前缀和数组: 给定一个数组nums,其前缀和数组prex定义为prex[i]表示为数组nums从起始位置到第i个位置的元素累加和。构建前…...
2024年最新版Ajax+Axios 学习【包含原理、Promise、报文、接口等...】
基础知识 AJAX概念 AJAX概念:是浏览器与服务器进行数据通信的技术。 认识URL 定义:统一资源定位符,简称网址,用于访问网络上的资源。 组成: http协议:超文本传输协议,规定浏览器和服务器之…...
【Qt线程】—— Qt线程详解
目录 (一)多线程的概述 (二)Qt线程的使用条件 (三)创建线程的方法 3.1 继承QTread,重写run()函数 3.1.1 为什么要重写 3.2 继承QObject 3.3 核心API介绍 3.4 关闭线程的使用方法 &…...
Golang | Leetcode Golang题解之第391题完美矩形
题目: 题解: func isRectangleCover(rectangles [][]int) bool {type point struct{ x, y int }area, minX, minY, maxX, maxY : 0, rectangles[0][0], rectangles[0][1], rectangles[0][2], rectangles[0][3]cnt : map[point]int{}for _, rect : range…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
