QSerialPort适应多线程应用的改进
来源:互联网 发布:苹果手机rar解压软件 编辑:程序博客网 时间:2024/06/09 14:40
类Unix系统的设备接口使用了基于select的事件驱动,这使得设备对象必须存在于某一个线程中,而因为select事件无法直接从设备跨线程传输,双工设备的跨线程操作也无法直接实现。Qt作为跨平台的开发库,为兼容类Unix系统的事件驱动,也设计为类似的限制。
对于全双工串口的QSerialPort对象来说,当数据传输压力较小时,直接在主线程中通过为readyRead信号编写槽函数可以很方便的实现数据接收,而主线程中与UI互动相关的数据发送工作也可以很好的执行;而当某一方向的数据传输压力较大时,由于主线程还必须响应UI的事件循环,这可能会造成该方向的数据传输产生延迟,因此必须移入子线程以避开主线程的UI事件循环,也就是说,双工设备的跨线程操作是必要的。
那么如何设计基于Qt实现双工设备的跨线程操作呢?以串口为例,可以考虑使用Qt的阻塞式跨线程信号槽连接机制,在操作发起线程中发射信号传入参数后等待槽函数执行完成;而在串口的信号连接的槽函数中调用对应的操作函数,并将函数结果存入类成员变量作为中转,然后返回;操作发起线程阻塞结束后,通过读取类成员变量来获取并输出操作结果。
以串口的write操作示例具体的实现方法,新的方法命名为threadSafeWrite,而从QSerialPort继承的自定义类命名为QThreadSafeSerialPort:
class QThreadSafeSerialPort : public QSerialPort{ Q_OBJECT Q_DISABLE_COPY(QThreadSafeSerialPort) QMutex m_mutex; qint64 m_resInt64;Q_SIGNALS: void sigWrite(char const* data);public: QThreadSafeSerialPort(QObject* parent = Q_NULLPTR) : QSerialPort(parent) { connect(this, &sigWrite, this, [&](char const* data){m_resInt64 = write(data);}, Qt::BlockingQueuedConnection); } qint64 threadSafeWrite(char const* data) { if (QThread::currentThread() == thread()) { return write(data); } else { QMutexLocker locker(&m_mutex); emit sigWrite(data); return m_resInt64; } }
由于使用了信号槽机制,QThreadSafeSerialPort 对象所在的线程必须使用事件循环,否则跨线程的threadSafeWrite调用仍然无法正确执行;而为了执行事件循环,QThread类的run函数中必须执行exec();exec()在事件循环结束之前不会返回,这使得在run函数中执行阻塞式数据接收或发送的方案变得无法实现,因此仍然不得不通过实现槽函数并连接readyRead/bytesWritten信号来实现数据接收/发送。为了让数据接收/发送的槽函数在子线程中执行,槽函数所依附的QObject继承类对象必须生存在子线程中。示例代码如下:
class ThreadSlotHelper : public QObject{ Q_OBJECTpublic: ThreadSlotHelper(void) : QObject(Q_NULLPTR) {}public Q_SLOTS: void onReadyRead(void) { // do read work... }};class ExampleThread : public QThread{ Q_OBJECT ThreadSlotHelper m_helperObj; QThreadSafeSerialPort m_port;protected: virtual void run(void) { m_port.open(QIODevice::ReadWrite); exec(); m_port.close(); }public: ExampleThread(QObject* parent = 0) : QThread(parent){ m_helperObj.moveToThread(this); m_port.moveToThread(this); connect(&m_port, SIGNAL(readyRead()), &m_helperObj, SLOT(onReadyRead())); }};
性能分析:虽然子线程中使用了事件循环,但由于子线程中并没有UI,不用执行UI事件,子线程所有的事件循环均可用于串口的通讯事件,因而子线程中的性能损失并不明显;跨线程的threadSafeWrite调用由于以阻塞式的信号连接方式连接信号和槽函数,需要跨线程等待事件循环的执行,其执行效率必然较为低下,这是使用跨线程信号槽机制所必须付出的代价;若threadSafeWrite的实现方案无法满足性能要求,则必须重新设计数据读写方案了。
- QSerialPort适应多线程应用的改进
- Qt:QSerialPort的多线程扩展
- QSerialPort
- QSerialPort
- 多线程 Pipeline 的改进
- 遗传算法(2):对适应度函数的改进
- 改进后的多线程爬虫
- PHP不能适应大型应用的理由
- 使用QT-QSerialport打不开串口的原因
- QserialPort 串口数据发送的问题
- 多线程的应用 多线程
- 改进后的多线程文件搜索
- 网易新闻——多线程的改进
- 改进PHP的var_dump()方法使之适应显示从数据库中查出来的数据
- [容易] 递归的应用:改进的汉诺塔
- 怎样开发适应不同分辨率的Android应用
- 移动应用开发的几大特性HTML5如何适应
- CMM3下的应用及改进
- Jquery输入框获得/失去焦点方式
- JAVA基本数据类型简介
- DOM学习笔记整理二使用及定时器
- IOS UIImageView
- 喋喋不休啊
- QSerialPort适应多线程应用的改进
- 如何优雅的在AndroidM中使用SD卡
- Tomcat目录结构及Tomcat Server处理一个http请求的过程
- hadoop之远程debug
- MySQL学习笔记---- LOAD DATA LOCAL INFILE中文乱码
- Qt应用程序图标的个性化设置
- 基于DBCP的数据库连接池对象
- pAdTy_-9 构建一个简单的用户界面和活动(Activity)
- XYZZY(spfa判环)