SerialPort在关闭后触发DataReceived事件

Ale*_*Sin 0 .net c# serial-port

尝试停止SerialPort时,我遇到了一种奇怪的行为:在取消订阅和调用之后,DataReceived事件继续触发close!(参见StopStreaming以下代码).因此,在我的事件处理程序代码中,我得到一个InvalidOperationException,并显示"端口已关闭"的消息.

我错过了什么?关闭端口和停止事件的正确方法是什么?

编辑:我每次运行代码时都会收到此错误.所以这不是随机发生的竞争条件,而是一个系统性问题,表明代码完全破碎!但是,我没看到怎么样......

private SerialPort comPort = new SerialPort();

public override void StartStreaming()
{
  comPort.Open();
  comPort.DiscardInBuffer();
  comPort.DataReceived += comPort_DataReceived;
}

public override void StopStreaming()
{
  comPort.DataReceived -= comPort_DataReceived;
  comPort.Close();
  isStreaming = false;
}

private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  if (e.EventType == SerialData.Chars)
  {
    SerialPort port = (SerialPort)sender;
    int N = comPort.BytesToRead;
    for (; N > 0; N--)
    {
      byte b = Convert.ToByte(comPort.ReadByte());
      //... process b
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:按照建议,我将StopStreaming代码更改为以下内容:

public override void StopStreaming()
{
  comPort.DataReceived -= comPort_DataReceived;
  Thread.Sleep(1000);
  comPort.DiscardInBuffer();
  Thread.Sleep(1000);
  comPort.Close();
  isStreaming = false;
}
Run Code Online (Sandbox Code Playgroud)

它现在似乎工作但我真的不高兴.我希望有一种更有效的方法来删除回调,而不是在程序中插入休眠期.

Han*_*ant 5

在线程池线程上调用DataReceived事件处理程序.是的,他们有一个在不可预测的时间运行代码的尴尬习惯,这不是即时的.因此,如果设备正在主动发送数据,它可以与您的Close()调用竞争并关闭它之后运行,这是相当不可避免的.取消订阅不能修复它,线程池线程已经获得了它的目标方法.

确实意识到你正在做什么来触发这个问题,你在设备发送数据关闭了端口.这不是很好,它可以保证导致数据丢失.但是,在调试代码时不太可能发生,因为您实际上并不关心数据.

对策是关闭握手,以便设备不再发送任何东西.丢弃输入缓冲区.然后睡一会儿,一两秒钟,以确保飞行中的任何线程池线程都已完成运行.然后关闭端口.一个非常务实的方法就是不关闭端口,Windows将在您的进程终止时处理它.