繁体   English   中英

C# Winform 在 SerialPort.Close 上冻结

[英]C# Winform freezing on SerialPort.Close

我有一个 winform 程序,它在SerialPort上执行一些异步 IO。 但是,我定期遇到程序在 SerialPort.Close() 调用时冻结的问题,似乎是随机的。

我认为这是一个线程安全问题,但我不确定如何解决它。 我尝试使用端口打开/关闭功能添加/删除异步 DataReceived 处理程序并丢弃端口上的输入和输出缓冲区,但它似乎没有做任何事情。 我认为重要的SerialPort代码如下:

using System;
using System.Collections.Generic;
using System.IO.Ports;

public class SerialComm
{
  private object locker = new object();

  private SerialPort port;
  private List<byte> receivedBytes;

  public SerialComm(string portName)
  {
    port = new SerialPort(portName);
    port.BaudRate = 57600;
    port.Parity = Parity.None;
    port.DataBits = 8;
    port.StopBits = StopBits.One;

    receivedBytes = new List<byte>();
  }

  public void OpenPort()
  {
    if(port!=null && !port.IsOpen){
      lock(locker){
        receivedBytes.Clear();
      }

      port.DataReceived += port_DataReceived;
      port.Open();
    }
  }

  public void ClosePort()
  {
    if(port!=null && port.IsOpen){
      port.DataReceived -= port_DataReceived;
      while(!(port.BytesToRead==0 && port.BytesToWrite==0)){
        port.DiscardInBuffer();
        port.DiscardOutBuffer();
      }
      port.Close();
    }
  }

  private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
  {
    try{
      byte[] buffer = new byte[port.BytesToRead];
      int rcvdBytes = port.Read(buffer, 0, buffer.Length);

      lock(locker){
        receivedBytes.AddRange(buffer);
      }

      //Do the more interesting handling of the receivedBytes list here.

    } catch (Exception ex) {
      System.Diagnostics.Debug.WriteLine(ex.ToString());
      //put other, more interesting error handling here.
    }
  }
}

更新

感谢@Afrin 的回答指出了 UI 线程的死锁情况( 这篇博文很好地描述了它,并提供了其他几个很好的提示),我做了一个简单的更改,但还没有能够重现错误!

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  try{
    byte[] buffer = new byte[port.BytesToRead];
    int rcvdBytes = port.Read(buffer, 0, buffer.Length);

    lock(locker){
      receivedBytes.AddRange(buffer);
    }

    ThreadPool.QueueUserWorkItem(handleReceivedBytes);

  } catch (Exception ex) {
    System.Diagnostics.Debug.WriteLine(ex.ToString());
    //put other, more interesting error handling here.
  }
}

private void handleReceivedBytes(object state)
{
  //Do the more interesting handling of the receivedBytes list here.
}

当您关闭它时它会挂起的原因是因为在您的 SerialPort 对象的事件处理程序中

您正在与主线程同步调用(通常通过调用 invoke)。 SerialPort 的 close 方法等待其 EventLoopRunner 线程触发 DataReceived/Error/PinChanged 事件终止。 但是由于您自己的事件代码也在等待主线程响应,因此您会遇到死锁情况。

解决方案:使用 begininvoke 而不是 invoke: https ://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application

参考: http : //stackoverflow.com/a/3176959/146622

编辑:Microsoft 链接已断开,因为他们已停用 Connect。 尝试web.archive.org: https://web.archive.org/web/20111210024101/https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application

nobugz 用户从这里解决方法:

1)添加System.Threading.Thread CloseDown; 与串口serialPort1形成的字段;

2) 使用serialPort1关闭步骤实现CloseSerialOnExit()方法:

private void CloseSerialOnExit()
{

    try
    {
        serialPort1.DtrEnable = false;
        serialPort1.RtsEnable = false;
        serialPort1.DiscardInBuffer();
        serialPort1.DiscardOutBuffer();
        serialPort1.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);

    }
}

3)当您需要关闭serialPort1 (例如单击按钮)时,请在新线程中调用CloseSerialOnExit()以避免挂起:

...
CloseDown = new System.Threading.Thread(new System.Threading.ThreadStart(CloseSerialOnExit)); 
CloseDown.Start();
...

就是这样!

我有同样的问题。 我通过使用SerialPortStrem库解决了这个问题。 您可以通过 Nuget Pageckage 安装程序进行安装。

SerialportStream 库具有以下优点。

  • System.IO.Ports.SerialPort 和 SerialStream 的独立实现,以获得更好的可靠性和可维护性。

使用 SerialPortStream 库后,我没有在 WPF 中出现死锁等 UI 冻结问题。 我认为 Windows 表单中存在同样的问题。 因此,请使用 SerialPortStream 库。

这个库显然是解决 UI 冻结的解决方案。

也谢谢你的回答。 直到今天我都遇到了类似的问题。 我使用了 Andrii Omelchenko 解决方案并帮助了很多,但不是 100%。 我看到挂串口的原因是接收事件处理程序。 在停止串口之前,卸载接收事件处理程序。

try
        {
            serialPort.DtrEnable = false;
            serialPort.RtsEnable = false;
            serialPort.DataReceived -= serialPort_DataReceived;
            Thread.Sleep(200);
            if (serialPort.IsOpen == true)
            {
                serialPort.DiscardInBuffer();
                serialPort.DiscardOutBuffer();
                serialPort.Close();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM