0

我在尝试停止 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
    }
  }
}

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

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

它现在似乎有效,但我并不那么高兴。我希望有一种更有效的方法来删除回调,而不是在程序中插入睡眠时间。

4

4 回答 4

5

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

请意识到您正在做什么来触发此问题,您在设备发送数据时正在关闭端口。这不是很好,它肯定会导致数据丢失。但在调试代码时并非不可能发生,因为您实际上并不关心数据。

一个对策是关闭握手,这样设备就不能再发送任何东西了。丢弃输入缓冲区。然后休眠一会儿,一两秒,以确保所有运行中的线程池线程都已完成运行。然后关闭端口。一个非常实用的方法是根本不关闭端口,当您的进程终止时,Windows 会处理它。

于 2012-12-14T10:59:29.593 回答
2

看起来像多线程问题。

SerialPort.DataReceived在线程池的工作线程中引发。您正在关闭线程的端口,这与SerialPort.DataReceived引发的线程不同。

你可以处理InvalidOperationException或编写一些同步代码来解决这个问题。

更新。

问题是,如果您的设备密集发送数据,则会将SerialPort越来越多的工作项排队到线程池中。即使您的代码会在之前的任何时间休眠Close,也可能是不够的。不幸的是,SerialPort有一个丑陋的实现。它没有一个选项,可以告诉“请停止向我发送垃圾邮件”。

因此,具体的解决方案取决于设备的协议和握手参数。

于 2012-12-14T10:25:12.553 回答
0

我一直在处理的应用程序中遇到了同样的问题。在这里阅读有关线程池如何实现它的信息令人兴奋。

不过,在我追查它的来源之前,我发现将 DataReceived 事件处理程序的内容包含在为try catch预期问题而编写的语句中是一种非常有效的解决方法。现在我知道如果我需要/想要在接收数据的同时关闭 SerialPort,我真的无法采取任何措施来防止该问题,我对这种方法非常满意。

于 2013-04-04T00:36:48.627 回答
0

当用户尝试退出应用程序而它仍在从连接的设备接收数据时,我遇到了类似的问题。应用程序在调用 Me.Close() 后抛出 System.IO.IOException。

我发现的最简单的解决方案是在 _FormClosing 事件处理程序中将 SerialPort ReceivedBytesThreshold 设置为一个大数字。这会降低 DataReceived 事件的频率,并为 Close() 调用提供时间,以便在 DataReceived 事件处理程序处于非活动状态时完成。

于 2021-11-05T14:15:05.017 回答