问题
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.
}
回答1:
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
reference: http://stackoverflow.com/a/3176959/146622
回答2:
I had a same problem. I solved this problem by using SerialPortStrem library. You can install by Nuget Pageckage Installer.
SerialportStream libary has the following advantages.
- An independent implementation of System.IO.Ports.SerialPort and SerialStream for better reliability and maintainability.
After using SerialPortStream library, I hadn't UI freezing problem such as deadlock in WPF. I think the same issue in Windows forms. so, use the SerialPortStream library.
This library is obviously a solution to solve the UI Freezing.
回答3:
Workaround by nobugz user from here:
1) add System.Threading.Thread CloseDown;
field to form with serial port serialPort1
;
2) implement method CloseSerialOnExit()
with serialPort1
close steps:
private void CloseSerialOnExit()
{
try
{
serialPort1.DtrEnable = false;
serialPort1.RtsEnable = false;
serialPort1.DiscardInBuffer();
serialPort1.DiscardOutBuffer();
serialPort1.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
3) when you need to close serialPort1
(e.g. on button click) call CloseSerialOnExit()
in new thread to avoid hang:
...
CloseDown = new System.Threading.Thread(new System.Threading.ThreadStart(CloseSerialOnExit));
CloseDown.Start();
...
and that's it!
回答4:
Thank you also for answers. I faced similar issue until today. I used the Andrii Omelchenko solution and helped a lot but not 100%. I saw that the cause for hanging serial port is the reception event handler. Before stopping serial port, uninstall reception event handler.
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);
}
来源:https://stackoverflow.com/questions/8843301/c-sharp-winform-freezing-on-serialport-close