我有一个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线程的死锁条件(This blog post很好地描述了它,并提供了其他一些好的提示),我做了一个简单的改变,但是还没能做到重现错误!
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.
}
答案 0 :(得分:15)
关闭它时它会挂起的原因是因为在你的SerialPort对象的事件处理程序中
您正在与主线程同步调用(通常通过调用invoke)。 SerialPort的close方法等待其EventLoopRunner线程,该线程触发DataReceived / Error / PinChanged事件终止。但由于事件中你自己的代码也在等待主线程响应,你会遇到死锁情况。
解决方案:使用begininvoke而不是invoke: https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application
答案 1 :(得分:0)
nobugz用户从here开始的解决方法:
1)添加System.Threading.Thread CloseDown;
字段以与串行端口serialPort1
一起形成;
2)使用CloseSerialOnExit()
关闭步骤实现方法serialPort1
:
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();
...
就是这样!
答案 2 :(得分:0)
我有同样的问题。我通过使用 SerialPortStrem 库解决了这个问题。 您可以通过Nuget Pageckage Installer进行安装。
SerialportStream库具有以下优点。
使用SerialPortStream库之后,我没有UI冻结问题,例如WPF中的死锁。我认为Windows表单中存在相同的问题。因此,请使用SerialPortStream库。
该库显然是解决UI冻结的解决方案。
答案 3 :(得分:0)
也感谢您的回答。直到今天,我都面临着类似的问题。我使用了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);
}