SerialPort.Read(....)不遵守ReadTimeOut。

3

有一个遗留代码中存在的bug,该代码与支付终端通信。

在开始新的支付之前,代码尝试清除SerialPort的内部读取缓冲区。

我将代码裁剪到最简。它使用.NET SerialPort类型。设置50ms的读取超时时间。然后它读取512字节,并继续这样做,直到不再读取字节或抛出TimeoutException为止。

我们添加了一些日志记录,显示第一个Read(...)方法的调用有时需要10-15分钟,即使超时时间为50ms。然后会抛出TimeoutException并且应用程序会继续执行。但在Read(...)期间,应用程序会挂起。

这并不总是发生,Windows 2000机器似乎更容易出现此错误。

public class Terminal
{
    private SerialPort _serialPort = new SerialPort();

    public void ClearReadBuffer()
    {
        try
        {   
            _serialPort.ReadTimeout = 50;
            int length;
            do
            {
                var buffer = new byte[512];
                length = _serialPort.Read(buffer, 0, 512);
            } while (length > 0);
        }
        catch (TimeoutException) {}
    }
}

非常感谢您的帮助。

PS:大多数错误报告来自连接到EdgePort的W2K机器,该设备模拟了一堆虚拟COM端口。其驱动程序创建了一堆(大约8个)本地COM端口。

但是我们也有来自Windows 7的报告。如果我们直接将设备连接到PC(没有EdgePort),我们也可以复制此问题。但不如经常发生,而且当发生延迟时不是10分钟,而是1-2分钟。

更新:尝试了很多方法来修复此问题。很难复制,但在现场由于分布在成千上万的PC上,它经常发生。实际上,用另一个开源版本替换了.NET 2.0 SerialPort类型。在我们能够实际复制它的一个PC上,工作得毫无问题,但遗憾的是,在生产中进行试点测试时,问题仍然继续发生。

支付终端的代码是几年前编写的,我将其移植到了另一个应用程序中。在移植过程中,我重构了一些代码,但保留了原始功能。与终端通信时,代码将:

  1. 从线程池中启动另一个线程
  2. 将消息发送到设备
  3. 从串行端口读取,直到收到响应或超时为止。

同时,主线程具有一个while循环,其中包含Thread.Sleep(50)和Application.DoEvents()调用(yuck!)。我重构了整个“等待循环”,并使用了WaitHandle(AutoResetEvent / ManualResetEvent)。我只是等到此句柄被设置。工作得毫无问题,但在某些PC上,所有串行端口通信都会冻结几分钟,直到触发某些内容。重新启用Application.DoEvents()工作方式,问题就解决了。

不幸的是,它仍然存在,对我来说是一个谜,为什么在这里需要它以及为什么会导致如此严重的副作用。该应用程序支持其他5种类型的串行端口设备。与这些设备通信从未需要过类似的内容。


不确定您是否看到了这篇文章:http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/dc8f858c-8f6f-4184-aac6-02ac88dc5e77。有没有可能是硬件/供应商特定的问题? - reuben
我认为这更多是与驱动程序有关。这种行为是否明确与操作系统相关?还是只是因为您的Win2000机器可能有一个有问题的UART +驱动程序? - zeFrenchy
不只是来自Windows 7机器的报告。因此,可能与操作系统本身无关。但是所有使用相同的EdgePort硬件/驱动程序。 - Christophe Geers
也许SerialPort类使用Windows消息(wndproc),因此如果您阻塞主线程,则需要DoEvents?在这种情况下(或者可能是任何情况),正确的解决方案是不要阻塞主线程。 - Herman
1个回答

7
也许在端口的“待读取字节数”上添加一个测试可以帮助避免糟糕编码的驱动程序。
length = (_serialPort.BytesToRead > 0) ? _serialPort.Read(buffer, 0, 512) : 0;

更好的方法是使用
_serialPort.DiscardInBuffer();

改为这样!


我希望 DiscardInBuffer 能解决这个问题,如果能做到就很好了。 - Peter
重写了ClearBufferMethod。首先清除了Windows驱动程序缓冲区(DiscardInBuffer),然后清除SerialPort的内部缓冲区(如果有任何内容)。这不再阻塞,因为大多数情况下没有调用Read(...)方法。但是,在随后的Write(...)中,SerialPort实例仍然会冻结5-10分钟。非常奇怪,最终用.NET(2.0)SerialPort类型进行了替换。现在工作正常。 - Christophe Geers
@Christophe Geers 我一直在处理一个听起来很像这个问题的难题。你是不是最终使用自己的实现方式重新编写了.NET串口类型?你还有什么更多的见解可以分享,关于问题的具体情况或哪些部分需要更改吗? - MickeyfAgain_BeforeExitOfSO
现在是2021年,我正在使用VS2019社区版。问题仍然存在...! - Azmeer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接