如何使用 COMMTIMEOUTS 等待字节可用但读取多个字节?

Posted

技术标签:

【中文标题】如何使用 COMMTIMEOUTS 等待字节可用但读取多个字节?【英文标题】:How do I use COMMTIMEOUTS to wait until bytes are available but read more than one byte? 【发布时间】:2012-04-30 20:28:49 【问题描述】:

我有一个 C++ 串行端口类,它具有非阻塞和阻塞模式用于读取操作。对于阻塞模式:

COMMTIMEOUTS cto;
GetCommTimeouts(m_hFile,&cto);
// Set the new timeouts
cto.ReadIntervalTimeout = 0;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;
SetCommTimeouts(m_hFile,&cto)

对于非阻塞模式:

COMMTIMEOUTS cto;
GetCommTimeouts(m_hFile,&cto);
// Set the new timeouts
cto.ReadIntervalTimeout = MAXDWORD;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;
SetCommTimeouts(m_hFile,&cto)

我想添加另一种模式,等待任意数量的字节并读取它们。

来自 MSDN COMMTIMEOUTS structure:

如果应用程序将 ReadIntervalTimeoutReadTotalTimeoutMultiplier 设置为 MAXDWORD 并将 ReadTotalTimeoutConstant 设置为大于零的值并且小于MAXDWORD,调用ReadFile函数时会出现以下情况之一:

如果输入缓冲区中有任何字节,ReadFile 会立即返回缓冲区中的字节。 如果输入缓冲区中没有字节,ReadFile 将等待直到 byte 到达,然后立即返回。 如果在指定的时间内没有字节到达 ReadTotalTimeoutConstant,ReadFile 超时。

这看起来像这样的代码:

COMMTIMEOUTS cto;
GetCommTimeouts(m_hFile,&cto);
// Set the new timeouts
cto.ReadIntervalTimeout = 100;
cto.ReadTotalTimeoutConstant = MAXDWORD;
cto.ReadTotalTimeoutMultiplier = MAXDWORD;
SetCommTimeouts(m_hFile,&cto)

但这会在第一个字节上返回。这是一个问题,因为我正在循环读取端口,并且字节的处理速度非常快,以至于下次我读取端口时,只有另一个字节可用。最终结果是我在循环中一次读取一个字节,并使用 100% 的内核运行该线程。

我想像 MSDN 文档中那样使用cto.ReadIntervalTimeout,但仍要等到至少一个字节可用。有人有想法吗?

谢谢。

【问题讨论】:

【参考方案1】:

你想要的行为将来自:

cto.ReadIntervalTimeout = 10;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;

它会在第一个字节中任意长阻塞(根据文档,通过将后两个字段设置为零来禁用总超时),然后只要数据流入,就会读取到缓冲区大小。如果有 10 毫秒的间隙在数据中,它将返回到目前为止已收到的内容。

【讨论】:

【参考方案2】:

如果您使用 100%(甚至接近)的 CPU,听起来您在其他地方做错了什么。正如我在previous answer 中展示的那样,多年来我一直使用将超时设置为 1 的代码。我最初将其设置为这种方式只是对可能至少可以工作的事情的疯狂猜测,目的是调整稍后。它工作得很好,以至于我根本没有时间调整它。举个例子,它会使用几乎无法估量的 CPU 时间从我的 GPS 读取输入(这是我唯一拥有的甚至不再模仿使用串行端口的东西)——经过数小时从GPS,它仍然显示使用的 CPU 时间为 0:00:00 秒(无论它是否正在运行,我都看不出 CPU 使用率有任何差异)。

现在,我肯定承认 GPS 不是(甚至接近于)最快的串行设备,但我们仍在谈论 ~100% 与 ~0%。这显然是一个非常严重的区别。

【讨论】:

我尝试了你的建议,但它只起到了一点作用。执行这项工作的 CPU 内核从几乎没有波动到 50% 到 100%,然后又随机返回。 那么,你在做什么来读取端口?很抱歉这么说,但你是唯一遇到这个问题的人 :( 处理串行端口使用的 CPU 太少以至于难以测量,就像@Jerry 说的那样。 @JerryCoffin 是对的——如果您一次读取一个字节并阻塞该字节,您不应该接近 100% 的 CPU。我有代码连续运行多个串行端口,一次一个字节,还有许多其他事情,但没有看到像 100% CPU 这样的东西。 将所有超时设置为 1 意味着行为取决于缓冲区大小(例如,如果将缓冲区大小 8000 传递给ReadFile,那么它将等待 8 秒)。【参考方案3】:
if (dwEvtMask == EV_RXCHAR )

   Sleep(1);
   if (dwLength > 2)
    
      Sleep(1);
      Readfile( m_Serial->m_hCom, data,dwLength, &dwBytesRead, &Overlapped);
      pDlg->PostMessage(WM_RECEIVE,0,0);
    

【讨论】:

无论您的超时设置如何,请尝试添加睡眠(1);在 Readfile() 之前到您的线程,这可以防止线程上 100% 的高 CPU 使用率..而不是永远循环检查。它为我工作! 如果你设置了正确的超时时间,睡眠是不必要的。 -1 用于使用EV_RXCHAR

以上是关于如何使用 COMMTIMEOUTS 等待字节可用但读取多个字节?的主要内容,如果未能解决你的问题,请参考以下文章

从端口读取时缺少 qserialport 数据,但返回 9 个可用字节

如何等待值然后返回一个对象

如何使编码未知的字节序列可用作 PHP 的输入?

如何解决高并发,连接等待超时的异常

ASP.Net Core - 登录后如何等待用户身份立即可用?

阻塞 recv() 返回小于请求字节的情况