简体   繁体   English

Qt串行端口的C ++线程模式

[英]C++ Threading Pattern for Qt serial port

My aim is to receive messages from a serial device without blocking the main thread (GUI) and to try to separate the platform-dependent logic (GUI and serial port) from the business logic (processing the messages) for ease of porting to other platforms 我的目的是在不阻塞主线程(GUI)的情况下从串行设备接收消息,并尝试将与平台相关的逻辑(GUI和串行端口)与业务逻辑(处理消息)分开,以便于移植到其他平台

Context: I'm using Qt, and the QtSerialPort module. 上下文:我正在使用Qt和QtSerialPort模块。 The message protocol is simple, 0xff is used to end each message. 消息协议很简单,0xff用于结束每个消息。

I've found 4 solutions so far: 到目前为止,我已经找到了4种解决方案:

Method 1: 方法1:

  1. Using one thread to read a serial port and fill a buffer 使用一个线程读取串行端口并填充缓冲区

  2. Using another thread to read the buffer, extract valid messages (into another buffer? not sure how this will work yet) 使用另一个线程读取缓冲区,提取有效消息(到另一个缓冲区中?不确定如何工作)

  3. Using yet another thread to parse the messages 使用另一个线程来解析消息

Method 2: 方法2:

  1. Using one thread to read a serial port, and extract valid messages into a buffer 使用一个线程读取串行端口,并将有效消息提取到缓冲区中

  2. Using another thread to parse the messages 使用另一个线程来解析消息

Method 3: 方法3:

  1. Using one thread to read a serial port, extract a valid message, and block till that message is processed, making use of QtSerialPort's internal read buffer to buffer incoming data 使用一个线程读取串行端口,提取有效消息,然后阻塞直到该消息被处理为止,利用QtSerialPort的内部读取缓冲区来缓冲传入的数据

Method 4: 方法4:

  1. Using the main thread to asynchronously read serial port, extract a valid message, and for each message, spawn a new thread to process them 使用主线程异步读取串行端口,提取一条有效消息,并为每条消息生成一个新线程来处理它们

Methods 1,2 and 3 differ by the number of threads the general workload is split up into, though I don't know which is best. 方法1,2和3的区别在于,一般工作负载被划分为多个线程,尽管我不知道哪个是最好的。

I'm currently using method 4, which is horribly inefficient and doesn't work well on lower-end computers, due to the enormous number of threads being spawned, and every time I move or interact with the GUI, serial communication halts. 我当前正在使用方法4,由于产生了大量线程,并且每次我移动或与GUI交互时,串行通信都会停止,方法4效率极低,并且在低端计算机上无法很好地工作。 Spawning a thread for each message also makes the order of the messages non-deterministic, which hasn't been a major problem so far... 为每条消息生成一个线程也会使消息的顺序不确定,这到目前为止并不是一个主要问题。

Are there other methods, what are the pros (if any) and cons of each, and which is the best to use? 是否还有其他方法,每种方法各有利弊(如果有),哪种方法最好? Thanks! 谢谢!

EDIT: A problem with processing messages in the main thread is that interacting with GUI (even moving the window) would block the message processing function. 编辑:在主线程中处理消息的一个问题是与GUI交互(甚至移动窗口)将阻止消息处理功能。 Is there any way around this? 有没有办法解决?

I think there are two main advantages that you can obtain by using multithreading: 我认为使用多线程可以获得两个主要优点:

  1. Avoiding poor GUI performance due to the GUI-handling routines being held off by the serial port processing routine 避免由于GUI处理例程被串行端口处理例程拖延而导致不良的GUI性能
  2. (perhaps more important) Avoid loss of serial data caused by buffer overflow when the GUI routines hold off the serial-data-reading routine for too long. (也许更重要)当GUI例程使串行数据读取例程拖延太长时间时,请避免由于缓冲区溢出而导致串行数据丢失。

You should only need to spawn a single thread. 您只需要产生一个线程即可。 Just have that thread read data from the serial port as it comes in (by connecting the QSerialPort's readyRead() signal to a slot that calls read() on the QSerialPort object), and then emit a signal (with a QByteArray argument) whenever it wants to send some serial data to the GUI. 只是让线程从串行端口中读取数据(通过将QSerialPort的readyRead()信号连接到调用QSerialPort对象上的read()的插槽),然后在任何时候发出信号(带有QByteArray参数)。想要将一些串行数据发送到GUI。 Your main/GUI thread can receive the data via a QueuedConnection that will not block either the serial-thread or the main/GUI thread. 您的main / GUI线程可以通过QueuedConnection接收数据,而QueuedConnection不会阻塞串行线程或main / GUI线程。

That's pretty much all there is to it; 这几乎就是全部了。 the only other thing to worry about is a clean shutdown. 唯一需要担心的是干净的关机。 Be sure to have another cross-thread signal/slot connection to the QThread's quit() slot, so that when it's time to quit, you can emit that signal and then call wait() on the QThread to wait for it to respond by going away. 确保与QThread的quit()插槽建立另一个跨线程信号/插槽连接,以便在需要退出时,可以发出该信号,然后在QThread上调用wait()以等待它通过以下方式响应远。 Once wait() has returned you can safely delete the QThread object. 一旦wait()返回,您就可以安全地删除QThread对象。

You can avoid additional threads at all by simply relying on Qt event loop (so far the main thread, the one also handling the GUI to be clear, will be blocked only when a message is actually received by the serial port). 您只需要依靠Qt事件循环就可以完全避免使用其他线程(到目前为止,只有当串行端口实际接收到一条消息时,主线程(还要处理GUI的主线程才被阻塞)。

Otherwise if you want to completely handle serial port in a dedicated thread, then the solution is to implement a class deriving from QThread and then override the run() function with something like this: 否则,如果您想在专用线程中完全处理串行端口,那么解决方案是实现一个从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();
}

Where readData is a function implemented in MyClass for handling the received data. 其中readData是在MyClass实现的用于处理接收到的数据的函数。 Since port is owned by the new thread (being created in run() ) then its events will be handled by the thread itself (in a completely independent manner with respect to the main thread). 由于port由新线程拥有(在run()创建),因此它的事件将由线程本身处理(相对于主线程完全独​​立)。

If you want at some point communicate something to the main thread (eg: you received something on serial which should cause a change in your GUI) then you can still use Qt's signals/slots. 如果您想在某个时候与主线程进行通讯(例如:您在串行上收到了某些内容,这可能会导致GUI发生更改),那么您仍然可以使用Qt的信号/插槽。 Simply implement a signal on MyClass and implement a slot on an object handled by the main thread (eg: your main form): then simply connect the signal for MyClass and the slot on your main form and you're done: signals/slots is THE solution for cross-thread communication in Qt. 只需在MyClass上实现一个信号,然后在由主线程处理的对象上实现一个插槽即可(例如:您的主窗体):然后只需将MyClass的信号与您的主窗体上的插槽连接起来就可以完成:signals / slots为Qt中跨线程通信解决方案。

You could also avoid using any (additional) threads and take advantage of Qt event loop . 您还可以避免使用任何(其他)线程,并利用Qt 事件循环 Read about events , QioDevice ; 了解事件QioDevice then Qt would pass your device file descriptor to its multiplexing loop (eg to poll(2) ....); 然后Qt会将您的设备文件描述符传递到其多路复用循环(例如,传递给poll(2) ....); probably QSocketNotifier should work (on Posix) on a non-socket file descriptor like a serial device. QSocketNotifier可能应该在非套接字文件描述符(如串行设备)上运行(在Posix上)。

Details are probably OS specific 详细信息可能是特定于操作系统的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM