使用c#提供的serialport类操作串口,串口的配置和回调函数的声明定义如下:
private SerialPort _EPL1023DAObj
_EPL1023DAObj.PortName = EPL1023DA_SerialPortNameCombox.Text;
_EPL1023DAObj.BaudRate = int.Parse(EPL1023DA_SerialPortBaudRateCombox.Text);
_EPL1023DAObj.DataBits = int.Parse(EPL1023DA_SerialPortDataBitCombox.Text);
switch (EPL1023DA_SerialPortStopBitCombox.SelectedIndex)
{
case 0:
_EPL1023DAObj.StopBits = StopBits.One;
break;
case 1:
_EPL1023DAObj.StopBits = StopBits.OnePointFive;
break;
case 2:
_EPL1023DAObj.StopBits = StopBits.Two;
break;
}
_EPL1023DAObj.Encoding = Encoding.GetEncoding("UTF-8");
_EPL1023DAObj.DataReceived += _EPL1023DASession_DataReceived;
private void _EPL1023DASession_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string recData = "";
if (!_EPL1023DAObj.IsOpen)
return;
try
{
if (_EPL1023DAObj != null && _EPL1023DAObj.IsOpen)
recData = _EPL1023DAObj.ReadLine();
// recData = _EPL1023DAObj.ReadExisting().ToString();
}
catch (TimeoutException) {
return;
}
if (_EPL1023AMFlag)
{
// 测试过程中 按照逗号解析 接收数据格式:"[命令],[数据]"
string[] recStr = recData.Split(',');
double.TryParse(recStr[0], out _EPL1023DAUncert);
_EPL1023DAUncertGetFlag = true;
}
else
{
this.Invoke(new Action(() =>
{
if(recData!="")
EPL1023DA_DataShowTxtbox.AppendText(_EPL1023DAObj.PortName + ":" + recData);
}));
}
这版代码在串口关闭时会直接卡死在 recData = _EPL1023DAObj.ReadLine(); 处,直接关闭软件时也会卡死,即使在关闭窗口的回调函数中增加了串口中断回调函数的解绑,串口缓存区的清除,串口资源的释放等操作,依然会导致软件卡死。
后根据个人猜测,可能和调用的ReadLine函数有关,遂将该函数替换为ReadExisting后,可以调用close将串口正常关闭了,但是如果不单独将串口关闭后再关闭窗口 依然会导致程序卡死。即使在窗口关闭的回调函数中调用关闭串口按钮的回调函数依然会导致串口卡死。以上现象均是建立在应用程序一直在接收数据的情况下,如果串口无数据接收,可以正常关闭串口和程序。
根据分析底层可能是UI界面同DataReceived事件处理程序是在lock (stream) {…}块中执行的SerialPort实例执行Close()方法时会先关闭SerialPort实例内部的SerialStream实例
SerialStream实例执行Close()方法时会lock实例自身当辅助线程调用DataReceived事件处理程序处理串口数据但还未更新界面时,点击界面“关闭”按钮调用SerialPort实例的Close()方法,UI线程会在lock(stream)处一直等待辅助线程释放stream的线程锁。当辅助线程处理完数据准备更新界面时问题来了,DataReceived事件处理程序中的this.Invoke()一直会等待UI线程来执行委托,但此时UI线程还停在SerialPort实例的Close()方法处等待DataReceived事件处理程序执行完成。
此时,线程死锁发生,两边都执行不下去了。遂将this.Invoke修改this.BeginInvoke,不等待UI线程执行完委托就返回,stream的线程锁会很快释放,SerialPort实例的Close()方法也无需等待
private void _EPL1023DASession_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string recData = "";
if (!_EPL1023DAObj.IsOpen)
return;
try
{
if (_EPL1023DAObj != null && _EPL1023DAObj.IsOpen)
//recData = _EPL1023DAObj.ReadLine();
recData = _EPL1023DAObj.ReadExisting().ToString();
}
catch (TimeoutException) {
return;
}
if (_EPL1023AMFlag)
{
// 测试过程中 按照逗号解析 接收数据格式:"[命令],[数据]"
string[] recStr = recData.Split(',');
double.TryParse(recStr[0], out _EPL1023DAUncert);
_EPL1023DAUncertGetFlag = true;
}
else
{/*
this.Invoke(new Action(() =>
{
if(recData!="")
EPL1023DA_DataShowTxtbox.AppendText(_EPL1023DAObj.PortName + ":" + recData);
}));
*/
this.BeginInvoke(new Action(() => {
if (recData != "")
EPL1023DA_DataShowTxtbox.AppendText(_EPL1033DAObj.PortName + ":" + recData);
}));
}
}