【问题标题】:SerialPort.Read(....) does not respect ReadTimeOutSerialPort.Read(....) 不尊重 ReadTimeOut
【发布时间】:2012-07-05 08:26:49
【问题描述】:

在一些与支付终端通信的遗留代码中存在错误。

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

我将代码精简到最低限度。它使用 .NET SerialPort 类型。设置了 50 毫秒的读取超时。然后它读取 512 个字节并继续这样做,直到不再读取字节或抛出 TimeoutException。

我们添加了一堆日志记录,它显示调用第一个 Read(...) 方法有时需要 10 到 15 分钟,即使超时时间为 50 毫秒。然后抛出 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:大多数错误报告来自 W2K 机器,其中设备连接到 EdgePort,它模拟了一堆虚拟 COM 端口。它的驱动程序创建了一堆(8 个左右)本地 COM 端口。

但我们也有来自 Windows 7 的报告。如果我们直接将设备连接到 PC(无 EdgePort),我们也可以重现该问题。但是没有那么频繁,当它发生时延迟不是 10 分钟,而是更像是 1 - 2 分钟。

更新:尝试了很多方法来解决这个问题。很难复制,但在现场经常发生,因为它分布在数千台 PC 上。实际上用另一个开源版本替换了 .NET 2.0 SerialPort 类型。在一台 PC 上工作没有问题,我们实际上可以在 60-70% 的时间内复制它。但可惜的是,在生产中的试点测试期间,问题仍然继续出现。

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

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

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

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

【问题讨论】:

  • 不确定您是否看过这篇文章:social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/…。有没有可能是特定于硬件/供应商的?
  • 我会认为这与驱动程序相关的内容比其他任何事情都重要。该行为是否肯定与操作系统有关?或者只是你的win2000机器可能有一个狡猾的UART +驱动程序?
  • 否,同样来自 Windows 7 机器的报告。所以可能不依赖于操作系统本身。但都使用相同的 EdgePort 硬件/驱动程序。
  • 也许 SerialPort 类使用 Windows 消息 (wndproc),因此如果您阻塞主线程,它需要 DoEvents?在那种情况下(或者在任何情况下),正确的解决方案是不阻塞主线程。

标签: c# serial-port


【解决方案1】:

也许在端口的“要读取的字节数”上添加测试可能有助于避开编码错误的驱动程序:

length = (_serialPort.BytesToRead > 0) ? _serialPort.Read(buffer, 0, 512) : 0;

最好还是用

_serialPort.DiscardInBuffer();

相反!

【讨论】:

  • 我希望 DiscardInBuffer 能解决这个问题,如果能解决的话,最好看看。
  • 重写了 ClearBufferMethod。首先清除 Windows 驱动程序缓冲区 (DiscardInBuffer),然后是 SerialPort 的内部缓冲区(如果有的话)。这不再被阻止,因为大多数时候 Read(...) 方法没有被调用。然而,在随后的 Write(...) 中,SerialPort 实例仍然冻结了 5 到 10 分钟。奇怪的东西,实际上最终取代了 .NET (2.0) SerialPort 类型。现在工作正常。
  • @Christophe Geers 我一直在处理一个听起来很像这样的问题。你的意思是你最终用你自己的实现重写了 .NET 串行端口类型?关于问题到底出在哪里或必须更改哪些部分,您是否还有其他见解要分享?
  • 现在是 2021 年,我正在使用 VS2019 社区版。问题依然存在......!
猜你喜欢
  • 2012-11-20
  • 2014-12-08
  • 2011-04-30
  • 2021-04-15
  • 2014-03-08
  • 1970-01-01
  • 2017-03-18
  • 2012-01-29
  • 1970-01-01
相关资源
最近更新 更多