Qt多线程通讯中信号阻塞问题及解决方案
创作时间:
作者:
@小白创作中心
Qt多线程通讯中信号阻塞问题及解决方案
引用
CSDN
1.
https://blog.csdn.net/chenai886/article/details/141959907
在多线程应用中,串口通讯或TCP通讯的场景常常涉及到持续的读写操作,如果子线程处理不当,可能会导致信号阻塞问题。本文将通过串口通讯或TCP通讯为例,详细解释如何在多线程环境中避免信号阻塞,并提供代码示例。
1. 问题背景
假设我们在一个应用程序中使用多线程处理串口或TCP通讯,通常会在子线程中实现持续的数据读取。为了确保实时处理数据,常见的做法是在子线程的 run() 方法中使用 while 循环。然而,如果没有正确处理事件循环,子线程可能会出现无法接收信号或阻塞的现象。
串口或TCP通讯的基本结构
通常,串口或TCP通讯的流程如下:
- 子线程负责监听串口或TCP端口,接收数据。
- 主线程通过信号槽机制向子线程发送控制命令。
- 子线程接收到命令后执行相应操作,并将结果通过信号传回主线程。
2. 常见信号阻塞现象
在没有处理事件循环的情况下,子线程执行如下代码:
示例:阻塞信号的串口通讯
class SerialThread : public QThread {
Q_OBJECT
public:
SerialThread(QObject *parent = nullptr) : QThread(parent) {
connect(this, &SerialThread::dataReceived, this, &SerialThread::handleData);
}
void run() override {
QSerialPort serial;
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); // 发出数据接收信号
}
}
}
signals:
void dataReceived(const QByteArray &data);
private slots:
void handleData(const QByteArray &data) {
qDebug() << "Data received in thread:" << data;
}
};
问题分析:
在上述代码中,子线程通过 while 循环不断监听串口数据,数据到达时发出 dataReceived 信号。然而,由于线程在 while 循环中没有进入事件循环,其他信号(例如来自主线程的控制命令)可能无法被处理,导致信号阻塞。
示例:阻塞信号的TCP通讯
class TcpThread : public QThread {
Q_OBJECT
public:
TcpThread(QObject *parent = nullptr) : QThread(parent) {
connect(this, &TcpThread::messageReceived, this, &TcpThread::handleMessage);
}
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); // 发出消息接收信号
}
}
}
signals:
void messageReceived(const QString &message);
private slots:
void handleMessage(const QString &message) {
qDebug() << "Message received in thread:" << message;
}
};
这里的问题与串口通讯类似,while 循环导致线程无法进入事件循环,可能会阻塞信号的处理。
3. 使用 QEventLoop 解决信号阻塞问题
为了避免信号阻塞,我们可以在 while 循环中使用 QEventLoop。这种方式确保了线程在执行任务的同时,仍然能够处理来自其他对象的信号。
示例:使用 QEventLoop 的串口通讯
class SerialThread : public QThread {
Q_OBJECT
public:
SerialThread(QObject *parent = nullptr) : QThread(parent) {
connect(this, &SerialThread::dataReceived, this, &SerialThread::handleData);
}
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(); // 进入事件循环以处理信号
}
}
signals:
void dataReceived(const QByteArray &data);
private slots:
void handleData(const QByteArray &data) {
qDebug() << "Data received in thread:" << data;
}
};
示例:使用 QEventLoop 的TCP通讯
class TcpThread : public QThread {
Q_OBJECT
public:
TcpThread(QObject *parent = nullptr) : QThread(parent) {
connect(this, &TcpThread::messageReceived, this, &TcpThread::handleMessage);
}
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(); // 进入事件循环以处理信号
}
}
signals:
void messageReceived(const QString &message);
private slots:
void handleMessage(const QString &message) {
qDebug() << "Message received in thread:" << message;
}
};
4. 总结
通过上述示例可以看出,在 Qt 的多线程通讯场景下,使用 while 循环容易导致信号的阻塞。引入局部事件循环(QEventLoop)可以有效解决这一问题,确保线程既能执行持续的任务,也能及时响应来自其他对象的信号。
使用局部事件循环的好处:
- 保持线程内任务的执行不被打断。
- 确保信号槽机制正常工作,信号不会被阻塞。
- 提升程序的响应性,特别是在处理通讯时尤为重要。
热门推荐
《语文课堂笔记整理攻略,预习复习必备神器》
详解DIY高性能主机装机:硬件推荐、防坑技巧与安装步骤
梁启超小便出血,中医:无碍,西医:切除肾脏,切除后才知是误诊
国学大师梁启超右肾被摘是医疗事故还是误诊?他为何替医院辩护?
芦荟怎样浇水才能长得茂盛?
小月子几天可以出门
B站如何取消视频小窗播放 B站关闭小窗口模式方法介绍
看守所的探视制度是怎样的
华图刷题怎么提高做题速度?
最喜欢赛车的十大明星 喜欢玩赛车的明星盘点 会开赛车的明星
从家庭卫生到健康守护:揭秘日本男性为何偏爱“蹲式排尿”
上班族用什么姿势午睡最健康
中国古人寿命:考古发现改写认知,中医果然功不可没
成都市金牛区沙河源街道 2024年“塑源计划”提案大赛行动圆满收官
乒乓球拍为啥一面红一面黑
对标国际,虚心学习:世界三大CNC砂轮强国
德国INS时尚博主Kathrin Bommann:高级感与自然随性的完美融合
银行的网上银行可以进行哪些投资理财操作?
宠物休克急救指南:兽医详解犬猫CPR关键步骤与要领
偏偏喜欢你,解读一段经典的音乐与情感之旅
失血过多喝大量水会怎么样
西安城墙城门全览:从隋唐到近现代的历史见证
全飞秒手术术后恢复期需要多久?
如何通过影线判断个股走势?
一次性医用口罩购买指南:从价格到技术指标的全方位解析
姜子牙:被誉为“百家之祖”的卓越智者
深入解析API技术:原理、实现与应用
《长津湖》没上榜,《阿凡达》仅排第二,盘点重映票房最高的电影
科普:鼻出血,我们该如何处理?
新能源汽车(锂电池)行业危化品安全管理解决方案