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

QT 多线程 管理串口

        记录一下自己使用多线程进行串口管理和数据读取的过程。如果有问题的话可以发消息给我。

背景

在使用QT制作一个串口数据读取处理的小软件的时候,发现了存在界面卡顿的情况,感觉性能太低,于是考虑把串口数据的读取和处理都放到子线程的缓冲区中,然后等到主线程需要的时候来使用。

设计

一开始使用了继承子QThread 的自定义worker类来进行管理,发现很不方便,还有各种安全问题,于是改用了只自定义worker类,然后在主线程中使用QThread,然后直接moveToThread的方法。

Worker类

class EMGWorker : public QObject
{Q_OBJECT
private:QSerialPort *m_serialPort;          // 串口指针QTimer *m_readTimer;                // 数据读取定时器QQueue<EMGDataFrame> m_emgBuffer;   // EMG数据缓冲区mutable QMutex m_bufferMutex;       // 缓冲区互斥锁static const int MAX_BUFFER_SIZE = 5000;static const int READ_INTERVAL_MS = 10;  // 10ms读取间隔public slots:void initialize(const QString &portName, int baudRate = 115200);void cleanup();void startReadingInThread();void stopReadingInThread();private slots:void readEMGData();  // 定时器触发的数据读取void onSerialError(QSerialPort::SerialPortError error);
};

大概设计如上,在UI中有打开串口的按钮来控制什么时候传递串口相关参数,还有搜集数据按钮来控制什么时候打开定时器进行读取。

初始化

主要内容就是等待打开串口按钮的事件触发之后才初始化Worker相关内容,注意定时器要在对应的工作线程中创建。

void EMGWorker::initialize(const QString &portName, int baudRate)
{// 在工作线程中创建串口m_serialPort = new QSerialPort(this);// 设置串口参数m_serialPort->setPortName(portName);m_serialPort->setBaudRate(baudRate);m_serialPort->setDataBits(QSerialPort::Data8);m_serialPort->setParity(QSerialPort::NoParity);m_serialPort->setStopBits(QSerialPort::OneStop);m_serialPort->setFlowControl(QSerialPort::NoFlowControl);// 连接串口信号connect(m_serialPort, &QSerialPort::errorOccurred,this, &EMGWorker::onSerialError);// 创建定时器(定时读取模式)m_readTimer = new QTimer(this);m_readTimer->setInterval(READ_INTERVAL_MS);connect(m_readTimer, &QTimer::timeout, this, &EMGWorker::readEMGData);// 打开串口if (m_serialPort->open(QIODevice::ReadWrite)) {emit connected();} else {emit errorOccurred("EMG串口打开失败: " + m_serialPort->errorString());}
}
线程间通信

在Qt中有一个重要规则:QObject及其子类的方法必须在创建该对象的线程中调用。

// 使用QMetaObject::invokeMethod实现线程安全调用
void EMGWorker::startReading()
{// Qt::QueuedConnection确保方法在目标对象所在的线程中异步执行QMetaObject::invokeMethod(this, "startReadingInThread", Qt::QueuedConnection);
}void EMGWorker::stopReading()
{QMetaObject::invokeMethod(this, "stopReadingInThread", Qt::QueuedConnection);
}

MainWindow控制

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{// 1. 创建EMG数据线程m_emgThread = new QThread(this);m_emgWorker = new EMGWorker();m_emgWorker->moveToThread(m_emgThread);// 2. 连接EMG线程信号connect(m_emgWorker, &EMGWorker::errorOccurred, this, &MainWindow::onEMGWorkerError);connect(m_emgThread, &QThread::finished, m_emgWorker, &EMGWorker::cleanup);
}

MainWindow析构

MainWindow::~MainWindow()
{isCollecting = false;// 断开所有信号连接if (m_emgWorker) {disconnect(m_emgWorker, nullptr, this, nullptr);m_emgWorker->stopReading();}// 等待线程结束if (m_emgThread && m_emgThread->isRunning()) {m_emgThread->quit();m_emgThread->wait(3000);}
}

ps:关于事件触发的子线程方法后续补充

http://www.lryc.cn/news/587218.html

相关文章:

  • Vue框架之计算属性与侦听器详解
  • 深入理解 LangChain:AI 应用开发的全新范式
  • openEuler欧拉系统重置密码
  • 标注识别 自己的数据集20张 roboflow 实例分割
  • 基于requests_html的爬虫实战
  • 【DVWA系列】——File Upload——low详细教程(webshell工具冰蝎)
  • Vue Router 完全指南:从入门到实战,高效管理前端路由
  • 12.I/O复用
  • 光盘处理难题不用愁,DVDFab 来救场
  • 博客项目 laravel vue mysql 第五章 标签功能
  • 从 Intel MacBook 迁移到 ARM MacBook 的完整指南
  • 【牛客刷题】四个选项:高考选择题方案统计(并查集+动态规划)
  • 【基于开源大模型(如deepseek)开发应用及其发展趋势的一点思考】
  • 时序预测 | Matlab代码实现VMD-TCN-GRU-MATT变分模态分解时间卷积门控循环单元多头注意力多变量时序预测
  • 【Linux系统】进程状态 | 进程优先级
  • 未来航空电子系统
  • C语言基础知识--文件读写(一)
  • 移动端设备本地部署大语言模型(LLM)
  • React强大且灵活hooks库——ahooks入门实践之状态管理类hook(state)详解
  • [Plecs基础知识系列]基于Plecs的半导体热仿真方法(实战篇)_1.建立电路模型
  • Linux修炼:开发工具
  • 《每日AI-人工智能-编程日报》--2025年7月12日
  • 使用Starrocks替换Clickhouse的理由
  • LeetCode经典题解:21、合并两个有序链表
  • Mybatis自动创建数据库表,并根据创建的表自动生成Mvc框架基础代码
  • CentOS系统下前后端项目部署攻略
  • Extended Nested Arrays for Consecutive Virtual Aperture Enhancement
  • C++——static成员
  • win10下的wsl2扩充空间
  • CUDA 编程笔记:使用 CUDA 加速矩阵乘法