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

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

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

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


http://www.kler.cn/a/294692.html

相关文章:

  • 后端:Aop 面向切面编程
  • CSP/信奥赛C++语法基础刷题训练(1):洛谷P5715 :三位数排序
  • 基于python 的opencv 使用GrabCut算法分割图像代码
  • 速盾:游戏盾的功能和原理详解
  • aws xray通过设置采样规则对请求进行过滤
  • SpringBoot整合Freemarker(三)
  • Docker配置Redis持久化
  • 如何保护服务器免受恶意软件攻击?
  • C++学习笔记(11)
  • 【网络安全】如何预防xss
  • 基于EPS32C3电脑远程开机模块设计
  • 飞思相机存储卡格式化数据如何恢复?提供全面指南
  • 风格控制水平创新高!南理工InstantX小红书发布CSGO:简单高效的端到端风格迁移框架
  • 【Nacos】负载均衡
  • 应用层简单实现udp / tcp网络通信
  • 「大数据分析」图形可视化,如何选择大数据可视化图形?
  • Qt: 详细理解delete与deleteLater (避免访问悬空指针导致程序异常终止)
  • JAVA并发编程JUC包之CAS原理
  • 概率学 笔记一 - 概率 - 随机变量 - 期望 - 方差 - 标准差(也不知道会不会有二)
  • 【AcWing】853. 有边数限制的最短路(bellman-ford贝尔曼福特算法)
  • 【HTML】script标签asyncdefer
  • 第十五届蓝桥杯青少组省赛成绩查询及国赛考试安排
  • 如何实现加密功能
  • 内置消息支持
  • 【电子通识】洁净度等级划分及等级标准
  • macOS 安装 Homebrew