关闭连接并取消订阅 DataReceived 处理程序

Closing connection and unsubscribing from DataReceived handler

我有一个应用程序可以通过串行端口从外部实验室单元接收数据。当我尝试关闭 SerialPort 时,出现以下错误:

System.IO.IOException: 'The I/O operation has been aborted because of either a thread exit or an application request.'

数据在事件处理程序中读取,由 SerialPort 事件引发 DataReceived - 默认情况下在单独的线程中运行(AFIK)。

当与 SerialPort 建立连接时,我订阅了 Eventhandler

我在关闭串口之前退订了EventHandler

关闭连接的代码(其中 _chronosPortSerialPort 的实例):

public void CloseConnection()
{
    if (_connected)
    {
        _chronosPort.DataReceived -= OnDataReceived;
        _chronosPort.Close();
        _connected = false;
     }
 }

private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
     var sp = (SerialPort) sender;
     RawSample = sp.ReadTo("\r");
     SampleFactory sampleFactory = new SampleFactory(RawSample, new SampleTypeExtractor());
     OnSampleReady(sampleFactory.GetSample());
}

调用 Close() 方法时,我得到:

'The I/O operation has been aborted because of either a thread exit or an application request.'

错误是从 EventHandlerSerialPort ReadTo() 方法抛出的,即使在关闭串行端口之前已经取消订阅事件处理程序。

这是因为您正在使用 SerialPort ReadToReadTo 一直处于阻塞状态,直到它在您指定的缓冲流中看到一个字符。所以当你去关闭端口的时候,ReadTo还在等待一个\r。取消订阅是不够的,因为它仍然 运行 在它自己的线程上。

修复方法是使用 ReadExisting(),这将立即 return 缓冲区中的所有字符。但是,这将需要您这边有更多的逻辑,您必须自己寻找结束字符。下面是使用 ReadExisting 的简单方法,我构建字符串直到看到结束字符,然后解析字符串:

StringBuilder sb = new StringBuilder();

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPort1.ReadExisting();

    foreach (char c in Data)
    {
        if (c == '\r')
        {
            sb.Append(c);

            CurrentLine = sb.ToString();
            sb.Clear();

            SampleFactory sampleFactory = new SampleFactory(CurrentLine, new SampleTypeExtractor());
            OnSampleReady(sampleFactory.GetSample());
        }
        else
        {
            sb.Append(c);
        }
    }
}