• 22806阅读
  • 17回复

[提问]求问QSerialPort在多线程环境下如何使用? [复制链接]

上一主题 下一主题
离线johnyork
 

只看楼主 倒序阅读 楼主  发表于: 2015-05-16
我想在子线程中使用QSerialPort对象接收串口数据,而在主线程中根据UI信号(如鼠标点击,等)用同一个QSerialPort对象向同一个串口发送命令,并且在串口长时间无数据接收的时候还可以在主线程强制close该对象,从而可以使子线程正常结束。
我尝试过在主线程中创建QSerialPort对象,然后在子线程中调用waitForReadyRead及read函数来读取从串口接收的数据,但似乎在某些情况下存在无法触发waitForReadyRead返回的问题,在Qt.io论坛上咨询后说是线程相关的问题,但未给出明确的解决方案。
浏览QSerialPort相关的帖子后,有一个说法是:QSerialPort对象必须在子线程中创建;于是我尝试了在子线程中new QSerialPort,虽然表面上看接收数据正常了,但是在主线程中打开或关闭端口时,QtCreator控制台均会显示3条消息:QWinEventNotifier: Event notifiers cannot be enabled or disabled from another thread,这说明调用方法中仍然存在问题。


PS:有人曾问过为什么不使用asynchronous mode,这是因为考虑到串口接收的数据其传输速率有点高(使用的MOXA串口卡),QSerialPort的asynchronous mode是在主线程的事件循环中完成数据的发送和接收,当UI显示控件较多或较复杂时,UI的绘制和渲染可能导致因数据接收延时而造成串口内部缓冲区溢出,或者接收数据丢失。因此,为尽可能降低数据丢失的概率,只能将数据接收工作置于子线程中,并应用QSerialPort的synchronous mode。


想问的问题:
1.有没有哪位高手遇到过和我类似的需求?有没有成功实现过?
2.成功实现过的高手,可否共享一下您关于QSerialPort的使用方法?
谢谢!
离线liulin188

只看该作者 1楼 发表于: 2015-05-17
和楼主同感啊,个人很不喜欢Qt那一套异步机制。我就喜欢阻塞同步+多线程来实现。

我写的多线程QextSerialPort都是在线程run函数里定义一个局部的栈对象
QextSerialPort extSerialPort;

就没有那个警告了。


https://wiki.qt.io/Qt_5.12_Release
https://wiki.qt.io/New_Features_in_Qt_5.12
https://wiki.qt.io/Qt_5.12.0_Known_Issues
https://www.qt.io/blog/qt-5.13.2-released
https://www.qt.io/blog/qt-creator-4.10.2-released
https://wiki.qt.io/Qt_5.12_Tools_and_Versions
离线liulin188

只看该作者 2楼 发表于: 2015-05-17
忘了提醒楼主一点,Qt自己的串口类问题很多很多,建议楼主使用第三方那个。。。
https://wiki.qt.io/Qt_5.12_Release
https://wiki.qt.io/New_Features_in_Qt_5.12
https://wiki.qt.io/Qt_5.12.0_Known_Issues
https://www.qt.io/blog/qt-5.13.2-released
https://www.qt.io/blog/qt-creator-4.10.2-released
https://wiki.qt.io/Qt_5.12_Tools_and_Versions
离线realfan

只看该作者 3楼 发表于: 2015-05-18
这个供参考,我写的多线程使用串口封装类。按自己的需要,局部调整一下就行了。
http://www.qtcn.org/bbs/read-htm-tid-58308.html
离线johnyork

只看该作者 4楼 发表于: 2015-05-19
谢谢各位高手的耐心回复。

我找了最新版的QextSerialPort(v1.2.0版,GitHub上下载的)来看,发现它除了提供事件驱动的非阻塞模式外,似乎没提供阻塞模式的串口API(我自己没找到?还是版本不正确?),而只有一个Polling即查询模式,而网上的教程则是用一个QTimer定时在子线程中查询是否有数据可读。不知是真是假,还请 @liulin188   解惑,感谢!

实在找不到QextSerialPort的阻塞模式用法下,我又重新开了一个测试工程并使用最精简的代码来测试Qt自带的QSerialPort,期望是因为我自己的代码导致的串口程序,没想到居然顺利接收了,再也没有遇到我之前碰到的无法触发waitForReadyRead返回的问题。最后仔细检查代码,重新看了一下Qt关于信号和槽的说明(因为原工程的接收线程中向主线程发射了信号),发现是因为在子线程中向主线程发射信号时传递参数使用的是栈上的临时对象造成的:子线程发射信号后并不会等待主线程中对应的槽函数执行,而是继续执行后面的工作,因而轮到主线程中的槽函数执行时,其传入的参数对象可能早就被销毁了,从而引起了内存访问越界,导致后面稀奇古怪的问题。

@realfan   您好,您的代码我大概看了一下,很有参考价值,非常感谢!
本帖提到的人: @realfan @liulin188
离线realfan

只看该作者 5楼 发表于: 2015-05-19
回 johnyork 的帖子
johnyork:谢谢各位高手的耐心回复。
我找了最新版的QextSerialPort(v1.2.0版,GitHub上下载的)来看,发现它除了提供事件驱动的非阻塞模式外,似乎没提供阻塞模式的串口API(我自己没找到?还是版本不正确?),而只有一个Polling即查询模式,而网上的教程则是用一个QTimer定时在子线程中查 .. (2015-05-19 00:24) 

QextSerialPort的CPU占用率太高了。
能用QSerialPort,还是尽量用Qt自带的吧
离线johnyork

只看该作者 6楼 发表于: 2015-05-19
回 realfan 的帖子
realfan:QextSerialPort的CPU占用率太高了。
能用QSerialPort,还是尽量用Qt自带的吧 (2015-05-19 09:27) 

我怀疑是它那个Polling模式搞的鬼
离线辉煌淘淘

只看该作者 7楼 发表于: 2015-06-03
初学QT,也是MOXA卡,我用多线程时,在 QThread 继承类中声明,然后在线程run函数里 serialport = new QSerialPort();
离线jienze

只看该作者 8楼 发表于: 2015-06-03
感觉还是自己用底层函数,写个串口类,保险!!!
欢迎交流合作~!!
离线johnyork

只看该作者 9楼 发表于: 2015-06-04
回 jienze 的帖子
jienze:感觉还是自己用底层函数,写个串口类,保险!!! (2015-06-03 16:34) 

我倒在前辈的代码基础上自己写过一个串口类,封装的Windows API,但是总觉得各种别扭各种不满意,现在决定使用Qt的现成类了,方便,省心^_^
离线johnyork

只看该作者 10楼 发表于: 2015-06-04
回 辉煌淘淘 的帖子
辉煌淘淘:初学QT,也是MOXA卡,我用多线程时,在 QThread 继承类中声明,然后在线程run函数里 serialport = new QSerialPort(); (2015-06-03 00:28) 

QSerialPort在多线程环境下需要注意几点:
1.同一种操作(读 or 写)一定要放在同一个线程进行,否则线程间冲突整死人
2.QSerialPort对象的打开和关闭必须在对象的创建线程中执行,否则它的事件通知功能无法正常执行,结果便是waitForReadyRead或waitForBytesWritten这样的等待函数无法正常工作
离线jienze

只看该作者 11楼 发表于: 2015-06-05
读串口是被动接收的,可以放在一个线程中,利用中断模式等待数据到来!
写串口是主动模式的,可以直接操作。问题是如果在不同线程中,怎样对同一个串口进行操作?
方法有很多种,
我用的的方法是在线程中,传递串口描述符指针。
欢迎交流合作~!!

只看该作者 12楼 发表于: 2016-02-29
我刚也遇到了同样的问题。
爱我你就抱抱我。
离线踢飞season

只看该作者 13楼 发表于: 2016-03-03
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x28fd74), parent's thread is QThread(0x15f10f68), current thread is testThread(0x24257ed8)
楼主,我把串口地址传到线程中总提示这个怎么解决
离线johnyork

只看该作者 14楼 发表于: 2016-03-03
回 踢飞season 的帖子
踢飞season:QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x28fd74), parent's thread is QThread(0x15f10f68), current thread is testThread(0x24257ed8)
楼主,我把串口地址传到线程中总提示这个怎么解决 (2016-03-03 12:47) 

你要用moveToThread把串口对象从主线程移动到QThread线程中才行。
离线zxwmail

只看该作者 15楼 发表于: 2019-07-05
    
离线xxbzls

只看该作者 16楼 发表于: 2021-12-14
mark
离线wocan23

只看该作者 17楼 发表于: 02-19
回 johnyork 的帖子
johnyork:你要用moveToThread把串口对象从主线程移动到QThread线程中才行。 (2016-03-03 23:36) 

能否来个demo?
快速回复
限100 字节
 
上一个 下一个