当前位置: 首页 > 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.dtcms.com/a/278017.html

相关文章:

  • 《[系统底层攻坚] 张冬〈大话存储终极版〉精读计划启动——存储架构原理深度拆解之旅》-系统性学习笔记(适合小白与IT工作人员)
  • springboot高校竞赛赛事管理系统 计算机毕业设计源码23756
  • Java行为型模式---策略模式
  • 第1章 概 述
  • dll文件缺失解决方法
  • C++——static成员
  • HiPPO: Recurrent Memory with Optimal Polynomial Projections论文精读(逐段解析)
  • QT控件命名简写
  • Linux内核高效之道:Slab分配器与task_struct缓存管理
  • 编译器优化——LLVM IR,零基础入门
  • 学习C++、QT---23(QT中QFileDialog库实现文件选择框打开、保存讲解)
  • 7月13日日记
  • 时间管理四象限理论
  • 小白学Python,操作文件和文件夹
  • 阶段性渗透总结
  • 第五章 Python手写数字识别【CNN卷积神经网络实现】
  • Windows怎样同步时间服务器?
  • 最简约的Windows多标签页文件管理器推荐 - 360文件夹 - 免费开源绿色软件推荐
  • Lucene原理
  • Android自定义View的事件分发流程
  • (33)记录描述窗体组件属性的枚举量 enum Qt :: WidgetAttribute, 简记为 WA_
  • Java结构型模式---外观模式
  • 和 *,以及 -> 和 .
  • C语言基础知识--柔性数组
  • 串口学习和蓝牙通信HC05(第八天)
  • LlamaIndex 检索器 Retriever
  • 题目V^V
  • 008_Claude_Code开发工具
  • 自注意力机制及其与早期注意力机制的区别
  • C++高频知识点(十)