当前位置: 首页 > news >正文

关于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)可以有效解决这一问题,确保线程既能执行持续的任务,也能及时响应来自其他对象的信号。

使用局部事件循环的好处:

  • 保持线程内任务的执行不被打断。
  • 确保信号槽机制正常工作,信号不会被阻塞。
  • 提升程序的响应性,特别是在处理通讯时尤为重要。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【数据库实战】1_Oracle_命中关联人或黑名单或反洗钱客户
  • MapSet之相关概念
  • Windows bat脚本学习九(srec_cat)
  • openGauss 之索引回表
  • 【Netty】netty中都是用了哪些设计模式
  • 端口安全老化细节
  • 软件测试之UI自动化测试
  • 被审稿人批得体无完肤?参考文献这样引用就对了!
  • 抠像拍照技术在展厅设计中的应用,实现了哪些新颖的互动体验?
  • uniapp vite3 require导入commonJS 的js文件方法
  • mysql可重复读不能解决幻读吗?
  • 安宝特科技 | AR眼镜在安保与安防领域的创新应用及前景
  • 实时地图+瞬移,黑神话地图工具来了
  • LLM模型:代码讲解Transformer运行原理
  • 快速排序的深入优化探讨
  • 〔开发系列〕一次关于小程序开发的深度总结
  • django开发-定时任务的使用
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • EOS是什么
  • exports和module.exports
  • Hexo+码云+git快速搭建免费的静态Blog
  • Spring Boot快速入门(一):Hello Spring Boot
  • Spring框架之我见(三)——IOC、AOP
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Twitter赢在开放,三年创造奇迹
  • yii2中session跨域名的问题
  • 从输入URL到页面加载发生了什么
  • 扑朔迷离的属性和特性【彻底弄清】
  • 如何设计一个比特币钱包服务
  • 如何优雅地使用 Sublime Text
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 赢得Docker挑战最佳实践
  • 硬币翻转问题,区间操作
  • 关于Android全面屏虚拟导航栏的适配总结
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​十个常见的 Python 脚本 (详细介绍 + 代码举例)
  • #define
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (一)appium-desktop定位元素原理
  • (已解决)Bootstrap精美弹出框模态框modal,实现js向modal传递数据
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)jQuery 基础
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .net mvc部分视图
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .Net插件开发开源框架
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • .project文件
  • @Transactional事务注解内含乾坤?
  • [2016.7.test1] T2 偷天换日 [codevs 1163 访问艺术馆(类似)]