C#Winform的冻结对SerialPort.Close [英] C# Winform freezing on SerialPort.Close

查看:165
本文介绍了C#Winform的冻结对SerialPort.Close的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有做一些异步IO上的的SerialPort 一个WinForm程序。不过,我定期运行到一个问题,程序冻结的SerialPort.Close()调用,似乎是随机的。

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

 使用系统;
使用System.Collections.Generic;
使用System.IO.Ports;

公共类SERIALCOMM
{
  私有对象更衣室=新的对象();

  私人的SerialPort口;
  私人列表<字节> receivedBytes;

  公共SERIALCOMM(字符串PORTNAME)
  {
    口=新的SerialPort(PORTNAME);
    port.BaudRate = 57600;
    port.Parity = Parity.None;
    port.DataBits = 8;
    port.StopBits = StopBits.One;

    receivedBytes =新的名单,其中,字节>();
  }

  公共无效OpenPort()
  {
    如果(端口=空&放大器;!&安培;!port.IsOpen){
      锁(柜){
        receivedBytes.Clear();
      }

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

  公共无效ClosePort()
  {
    如果(端口=空&放大器;!&安培; port.IsOpen){
      port.DataReceived  -  = port_DataReceived;
      而((port.BytesToRead == 0安培;!&安培; port.BytesToWrite == 0)){
        port.DiscardInBuffer();
        port.DiscardOutBuffer();
      }
      port.Close();
    }
  }

  私人无效port_DataReceived(对象发件人,SerialDataReceivedEventArgs E)
  {
    尝试{
      byte []的缓冲区=新的字节[port.BytesToRead]
      INT rcvdBytes = port.Read(缓冲液,0,buffer.Length);

      锁(柜){
        receivedBytes.AddRange(缓冲液);
      }

      //执行receivedBytes清单更有趣的处理在这里。

    }赶上(例外前){
      System.Diagnostics.Debug.WriteLine(ex.ToString());
      //把其他更有趣的错误处理。
    }
  }
}
 

更新

感谢@ Afrin的回答指出了死锁情况与UI线程(<一href="http://blogs.msdn.com/b/bclteam/archive/2006/10/10/top-5-serialport-tips-_5b00_kim-hamilton_5d00_.aspx">This博客文章做得很好描述它,并给出了其他几个很好的提示),我做了一个简单的改变,并没有能够重现错误呢!

 私人无效port_DataReceived(对象发件人,SerialDataReceivedEventArgs E)
{
  尝试{
    byte []的缓冲区=新的字节[port.BytesToRead]
    INT rcvdBytes = port.Read(缓冲液,0,buffer.Length);

    锁(柜){
      receivedBytes.AddRange(缓冲液);
    }

    ThreadPool.QueueUserWorkItem(handleReceivedBytes);

  }赶上(例外前){
    System.Diagnostics.Debug.WriteLine(ex.ToString());
    //把其他更有趣的错误处理。
  }
}

私人无效handleReceivedBytes(对象状态)
{
  //执行receivedBytes清单更有趣的处理在这里。
}
 

解决方案

当你关闭它是因为在你的SerialPort对象的事件处理程序

它会挂起的原因

您正与之同步的主线程调用(通常是通过调用调用)。的SerialPort的close方法等待其EventLoopRunner线程,触发DataReceived /错误/ PinChanged事件终止。但由于自己的code事件也在等待主线程响应,就碰上了死锁情况。

解决方案:使用调用的BeginInvoke的,而不是: <一href="https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application">https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application

I have a winform program that does some asynchronous IO on a SerialPort. However, I'm periodically running into an issue with the program freezing on the SerialPort.Close() call, seemingly at random.

I think it's a thread safety issue, but I'm not sure how to fix it if it is. I tried adding/removing the async DataReceived handler with the port open/close functions and discarding the in and out buffers on the port, but it doesn't seem to do anything. I think the important SerialPort code is below:

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.
    }
  }
}

UPDATE

Thanks to @Afrin's answer pointing out the deadlock condition with the UI thread (This blog post does a good job describing it, and gives several other good tips), I made a simple change, and haven't been able to reproduce the error yet!

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.
}

解决方案

The reason it would hang when you close it is because in the event handler of your SerialPort object

you're synchronizing a call with the main thread (typically by calling invoke). SerialPort's close method waits for its EventLoopRunner thread which fires DataReceived/Error/PinChanged events to terminate. but since your own code in the event is also waiting for main thread to respond, you run into a dead lock situation.

solution: use begininvoke instead of invoke: https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application

这篇关于C#Winform的冻结对SerialPort.Close的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆