我的目标是在不阻塞主线程(GUI)的情况下从串行设备接收消息,并尝试将平台相关逻辑(GUI和串行端口)与业务逻辑(处理消息)分开以便于移植到其他平台
上下文:我正在使用Qt和QtSerialPort模块。消息协议很简单,0xff用于结束每条消息。
到目前为止,我找到了4个解决方案:
方法1:
使用一个线程读取串口并填充缓冲区
使用另一个线程来读取缓冲区,提取有效的消息(进入另一个缓冲区?不知道这将如何工作)
使用另一个线程来解析消息
方法2:
使用一个线程读取串口,并将有效消息提取到缓冲区
使用另一个线程来解析消息
方法3:
方法4:
方法1,2和3的不同之处在于一般工作负载被分成的线程数,但我不知道哪个最好。
我目前正在使用方法4,由于产生了大量的线程,每次我移动或与GUI交互,串行通信都非常低效且在低端计算机上无法正常工作暂停。为每条消息生成一个线程也会使消息的顺序不确定,这到目前为止还不是一个主要问题......
是否有其他方法,每种方法的优点(如果有的话)和缺点是什么,哪种方法最好?谢谢!
编辑:在主线程中处理消息的一个问题是与GUI交互(甚至移动窗口)会阻塞消息处理功能。有没有办法解决这个问题?答案 0 :(得分:4)
我认为使用多线程可以获得两个主要优势:
您应该只需要生成一个线程。只需让该线程从串口读取数据(通过将QSerialPort的readyRead()信号连接到调用QSerialPort对象上的read()的插槽),然后每当它发出一个信号(带有QByteArray参数)想要将一些串行数据发送到GUI。您的主/ GUI线程可以通过QueuedConnection接收数据,该QueuedConnection不会阻塞串行线程或主/ GUI线程。
这就是它的全部内容;唯一需要担心的是干净关机。确保在QThread的quit()插槽中有另一个跨线程信号/插槽连接,这样当它退出时,你可以发出该信号然后在QThread上调用wait()以等待它响应远。一旦wait()返回,您可以安全地删除QThread对象。
答案 1 :(得分:2)
您可以通过简单地依赖Qt事件循环来避免额外的线程(到目前为止主线程,也是处理GUI清除的线程,只有在串口实际接收到消息时才会被阻止)。
否则,如果你想在专用线程中完全处理串口,那么解决方案就是实现一个派生自QThread
的类,然后用这样的东西覆盖run()
函数:
void MyClass::run()
{
QSerialPort port;
// ... serial port initialization here
// Connect signals/slots
connect(&port, SIGNAL(readyRead()), this, SLOT(readData()));
port.open();
// Start a new message loop on this thread
exec();
}
其中readData
是MyClass
中用于处理接收数据的函数。由于port
由新线程拥有(在run()
中创建),因此其事件将由线程本身处理(以与主线程完全独立的方式)。
如果您希望在某些时候与主线程进行通信(例如:您在串行上收到某些内容会导致GUI更改),那么您仍然可以使用Qt的信号/插槽。只需在MyClass
上实现一个信号,并在主线程处理的对象上实现一个插槽(例如:您的主表单):然后只需连接MyClass
的信号和主表单上的插槽即可。 #39; re done:signal / slots是 解决方案,用于Qt中的跨线程通信。
答案 2 :(得分:0)
您还可以避免使用任何(其他)线程并利用Qt event loop。阅读events,QioDevice;那么Qt会将你的设备文件描述符传递给它的多路复用循环(例如poll(2) ....);可能QSocketNotifier应该在非套接字文件描述符(如串行设备)上工作(在Posix上)。
详细信息可能是特定于操作系统的