QSerialPort::waitForBytesWritten "subtle errors" 有事件循环?
QSerialPort::waitForBytesWritten "subtle errors" with an event loop?
在 an answer on another question 中声称,当使用带有 QSerialPort
的主 Qt 事件循环时,QSerialPort::waitForBytesWritten
会导致 "subtle errors",因为它打开了另一个事件循环。
我正在使用 waitForBytesWritten
方法来确保在写入所有字节之前不会重新进入我的主事件循环。这不是执行此操作的有效方法吗? "subtle errors" 会发生什么?我应该改用 flush()
吗,这是否会确保我的事件循环 而不是 再次输入,直到所有待处理的字节都被写入?
我 在我的应用程序中看到一些与其串行端口通信相关的奇怪行为,但我不确定是什么 "layer" 通信导致了这个问题,所以它甚至可能与我使用 QSerialPort
无关。目前,关于它的起源,我唯一的提示是 QSerialPort
在我看到不良行为开始之前不久发出 ResourceError
。这个 ResourceError
和我使用 waitForBytesWritten
有关系吗?
我在 Debian 7 系统上使用 Qt 5.5.1。
在 Unix 上,读取通知将 EAGAIN
、EIO
和 EBADF
映射到 ResourceError
并停止处理进一步的读取通知,直到您调用 waitFor
功能或直到您重新打开端口。这使得 QSerialPort
无法读取,直到您重新打开或 waitFor
。将 EIO
和 EBADF
映射到同一个错误可能是有意义的。但是,由于 EAGAIN
是良性的,因此以这种方式对待它看起来像是一个 Qt 错误。
要查看是否是问题所在,您可以清除错误并重新调用 waitForReadyRead(0)
或 waitForBytesWritten(0)
并查看错误是否再次出现:
bool retryWaitForBytesWritten(int n, int retries = 10) {
if (!dev.bytesToWrite()) return false;
while (retries-- && !dev.waitForBytesWritten(n)) {
if (dev.error() != QSerialPort::ResourceError)
return false;
dev.clearError(); // retry if it was a resource error
}
return true;
}
你主要不应该使用 waitForBytesWritten
因为:
它并不像您想象的那样。它所确保的只是 QSerialPort
中的内部写入缓冲区已通过使用 write
系统调用将数据提供给操作系统而被清空。这并不意味着数据已经物理发送。
它可能会阻塞。不能保证阻止。
通常不需要通知写入何时完成。如果你有一个半双工(本质上)命令-响应协议,你不关心什么时候完成发送,而是什么时候响应到达或超时。
如果您有发送轮询命令的流协议并且不想生成它们的速度快于设备可以使用它们的速度,则您应该发出命令(并在数据结构中将它们标记为“已发出”)直到 bytesToWrite
超过高水位线。然后暂停添加新命令,直到收到 bytesWritten
信号通知发送缓冲区处于低水位线。 readyRead
插槽匹配对发出的命令的响应并适当地指示它们。
因此,理想情况下,您的代码应包含零个 waitXxx
调用。我们周围的世界是异步的。您不会等待事情发生,您会对它们做出反应。在发生某些事情之前不要停止事件循环——这是倒退的。在您等待的那件事之前可能会发生其他事情,直到那时您才忽略它们。即使你将 I/O 推到一个单独的线程,你仍然在浪费整个线程的阻塞。在顺序伪同步代码中处理重定向控制流的事件也很困难——一切最终都充满了(必要的!)错误检查。有时人们为此求助于使用异常,这可能没问题,但在大型代码库中很难正确执行,并且任何时候您希望将控制重定向到不在调用堆栈上的代码。当您将代码显式表示为分层状态机时,在面对异步事件(例如传入数据或错误)时更容易推断代码的正确性。
设计您的系统,使其根据异步发生的事件进行。如果您的 UI 的某些方面必须指示正在发送的数据的状态,只需这样做:不要等待,不要阻塞线程。
demonstrates how to implement asynchronous I/O for a device based on QIODevice
, using the state machine framework. Once you've got your states set up, it's a relatively easy matter to tie them to UI behaviors. See these answers: one, , three, four, five, six, seven, .
另外,我错了,我修正了另一个答案。 QSerialPort
不会重新进入事件循环,网络模块中的套接字也不会。
在 an answer on another question 中声称,当使用带有 QSerialPort
的主 Qt 事件循环时,QSerialPort::waitForBytesWritten
会导致 "subtle errors",因为它打开了另一个事件循环。
我正在使用 waitForBytesWritten
方法来确保在写入所有字节之前不会重新进入我的主事件循环。这不是执行此操作的有效方法吗? "subtle errors" 会发生什么?我应该改用 flush()
吗,这是否会确保我的事件循环 而不是 再次输入,直到所有待处理的字节都被写入?
我 在我的应用程序中看到一些与其串行端口通信相关的奇怪行为,但我不确定是什么 "layer" 通信导致了这个问题,所以它甚至可能与我使用 QSerialPort
无关。目前,关于它的起源,我唯一的提示是 QSerialPort
在我看到不良行为开始之前不久发出 ResourceError
。这个 ResourceError
和我使用 waitForBytesWritten
有关系吗?
我在 Debian 7 系统上使用 Qt 5.5.1。
在 Unix 上,读取通知将 EAGAIN
、EIO
和 EBADF
映射到 ResourceError
并停止处理进一步的读取通知,直到您调用 waitFor
功能或直到您重新打开端口。这使得 QSerialPort
无法读取,直到您重新打开或 waitFor
。将 EIO
和 EBADF
映射到同一个错误可能是有意义的。但是,由于 EAGAIN
是良性的,因此以这种方式对待它看起来像是一个 Qt 错误。
要查看是否是问题所在,您可以清除错误并重新调用 waitForReadyRead(0)
或 waitForBytesWritten(0)
并查看错误是否再次出现:
bool retryWaitForBytesWritten(int n, int retries = 10) {
if (!dev.bytesToWrite()) return false;
while (retries-- && !dev.waitForBytesWritten(n)) {
if (dev.error() != QSerialPort::ResourceError)
return false;
dev.clearError(); // retry if it was a resource error
}
return true;
}
你主要不应该使用 waitForBytesWritten
因为:
它并不像您想象的那样。它所确保的只是
QSerialPort
中的内部写入缓冲区已通过使用write
系统调用将数据提供给操作系统而被清空。这并不意味着数据已经物理发送。它可能会阻塞。不能保证阻止。
通常不需要通知写入何时完成。如果你有一个半双工(本质上)命令-响应协议,你不关心什么时候完成发送,而是什么时候响应到达或超时。
如果您有发送轮询命令的流协议并且不想生成它们的速度快于设备可以使用它们的速度,则您应该发出命令(并在数据结构中将它们标记为“已发出”)直到
bytesToWrite
超过高水位线。然后暂停添加新命令,直到收到bytesWritten
信号通知发送缓冲区处于低水位线。readyRead
插槽匹配对发出的命令的响应并适当地指示它们。
因此,理想情况下,您的代码应包含零个 waitXxx
调用。我们周围的世界是异步的。您不会等待事情发生,您会对它们做出反应。在发生某些事情之前不要停止事件循环——这是倒退的。在您等待的那件事之前可能会发生其他事情,直到那时您才忽略它们。即使你将 I/O 推到一个单独的线程,你仍然在浪费整个线程的阻塞。在顺序伪同步代码中处理重定向控制流的事件也很困难——一切最终都充满了(必要的!)错误检查。有时人们为此求助于使用异常,这可能没问题,但在大型代码库中很难正确执行,并且任何时候您希望将控制重定向到不在调用堆栈上的代码。当您将代码显式表示为分层状态机时,在面对异步事件(例如传入数据或错误)时更容易推断代码的正确性。
设计您的系统,使其根据异步发生的事件进行。如果您的 UI 的某些方面必须指示正在发送的数据的状态,只需这样做:不要等待,不要阻塞线程。
QIODevice
, using the state machine framework. Once you've got your states set up, it's a relatively easy matter to tie them to UI behaviors. See these answers: one,
另外,我错了,我修正了另一个答案。 QSerialPort
不会重新进入事件循环,网络模块中的套接字也不会。