C#中如果涉及到多线程,特别是大量的数据处理和界面更新时,如果简单强制的关闭串口,很可能会造成串口死掉,我1年来一直有个想法,今天终于真正找到了原因和解决的办法。
串口无法关闭的原因是:要关闭串口的时候,有其它线程还在读取数据或者更新界面。
关键是:在准备关闭串口的时候,看看是否在接收和处理数据,如果是就等它处理完为止;在事件处理的最前面,判断如果是准备关闭串口的bool类型值,就不再进入数据接收和处理。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Timers;
namespace PortTesting
{
/// /// 定义了一个委托类型
/// public delegate void WhenGetNew();
/// /// 串口封装类,Help By Wyz
/// public class PortDataDisplay
{
/// /// 系统串口类
/// public SerialPort serialPort = new SerialPort("COM1", 19200);
/// /// 解析得到数据后触发事件
/// public event WhenGetNew whenGetNew;
/// /// 处理线程
/// private SerialDataReceivedEventHandler threadCallHandler;
/// /// 对外的数据类型定义
/// public string dataSrc = "";
/// /// 准备关闭串口=true
/// private bool m_IsTryToClosePort = false;
/// /// true表示正在接收数据
/// private bool m_IsReceiving = false;
/// /// 初始化
/// public PortDataDisplay()
{
}
/// /// 有参数的构造函数
/// /// 串口号,如"COM1" /// 波特率,如19200 public PortDataDisplay(string PortName, int BaudRate)
{
serialPort = new SerialPort(PortName, BaudRate);
}
/// /// 开始工作
/// public void ConnectDeveice()
{
//0.注册事件
serialPort.DataReceived -= OnSerialPortDataCome;
serialPort.DataReceived += OnSerialPortDataCome;
//1.再设置一下串口参数
if (this.serialPort.IsOpen == false)
{
this.serialPort.ReadBufferSize = 1000;
this.serialPort.ReceivedBytesThreshold = 1;//数据达到120的时候才就要触发事件,不行!!应该是数据来就触发
//2.打开串口开始工作
m_IsTryToClosePort = false;
this.serialPort.Open();
}
}
/// /// 结束工作
/// public void DisconnectDeveice() // 关键和核心!!!
{
m_IsTryToClosePort = true;
while (m_IsReceiving)
{
System.Windows.Forms.Application.DoEvents();
}
serialPort.Close();
}
/// /// 当通知到有数据达到120时处理(读取,与分析)
/// /// /// private void OnSerialPortDataCome(object sender, SerialDataReceivedEventArgs e)
{
if (m_IsTryToClosePort) // 关键!!!
{
return;
}
m_IsReceiving = true; // 关键!!!
try
{
if (threadCallHandler == null)
{
threadCallHandler = new SerialDataReceivedEventHandler(OnSerialPortDataCome);
}
//read
dataSrc = serialPort.ReadExisting();//读出缓冲区所有数据
if (dataSrc != "" && this.whenGetNew != null)
{
this.whenGetNew();
}
}
finally // 放在finally里面比较好。
{
m_IsReceiving = false; // 关键!!!
}
}
}
}
使用的时候:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace PortTesting
{
public partial class FrmMain : Form
{
/// /// 封装好的串口类
/// private PortDataDisplay m_portDispl = new PortDataDisplay("COM1", 19200);
public FrmMain()
{
InitializeComponent();
}
private void btnOpen_Click(object sender, EventArgs e)
{
if (btnOpen.Text == "打开串口")
{
m_portDispl.whenGetNew -= portDispl_whenGetNew;
m_portDispl.whenGetNew += new WhenGetNew(portDispl_whenGetNew);
m_portDispl.ConnectDeveice();
btnOpen.Text = "关闭串口";
}
else if (btnOpen.Text == "关闭串口")
{
m_portDispl.DisconnectDeveice();
btnOpen.Text = "打开串口";
}
}
/// /// 事件
/// private void portDispl_whenGetNew()
{
WhenGetNew ehan = delegate
{
txtDisplay.AppendText(m_portDispl.dataSrc);
};
try
{
if (InvokeRequired)
{
this.Invoke(ehan);
}
}
catch
{
}
}
private void btnClear_Click(object sender, EventArgs e)
{
txtDisplay.Clear();
}
}
}
串口无法关闭的原因是:要关闭串口的时候,有其它线程还在读取数据或者更新界面。
关键是:在准备关闭串口的时候,看看是否在接收和处理数据,如果是就等它处理完为止;在事件处理的最前面,判断如果是准备关闭串口的bool类型值,就不再进入数据接收和处理。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Timers;
namespace PortTesting
{
///
///
///
///
{
///
///
///
///
///
///
///
///
///
///
///
///
///
///
{
}
///
///
{
serialPort = new SerialPort(PortName, BaudRate);
}
///
///
{
//0.注册事件
serialPort.DataReceived -= OnSerialPortDataCome;
serialPort.DataReceived += OnSerialPortDataCome;
//1.再设置一下串口参数
if (this.serialPort.IsOpen == false)
{
this.serialPort.ReadBufferSize = 1000;
this.serialPort.ReceivedBytesThreshold = 1;//数据达到120的时候才就要触发事件,不行!!应该是数据来就触发
//2.打开串口开始工作
m_IsTryToClosePort = false;
this.serialPort.Open();
}
}
///
///
{
m_IsTryToClosePort = true;
while (m_IsReceiving)
{
System.Windows.Forms.Application.DoEvents();
}
serialPort.Close();
}
///
///
{
if (m_IsTryToClosePort) // 关键!!!
{
return;
}
m_IsReceiving = true; // 关键!!!
try
{
if (threadCallHandler == null)
{
threadCallHandler = new SerialDataReceivedEventHandler(OnSerialPortDataCome);
}
//read
dataSrc = serialPort.ReadExisting();//读出缓冲区所有数据
if (dataSrc != "" && this.whenGetNew != null)
{
this.whenGetNew();
}
}
finally // 放在finally里面比较好。
{
m_IsReceiving = false; // 关键!!!
}
}
}
}
使用的时候:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace PortTesting
{
public partial class FrmMain : Form
{
///
///
public FrmMain()
{
InitializeComponent();
}
private void btnOpen_Click(object sender, EventArgs e)
{
if (btnOpen.Text == "打开串口")
{
m_portDispl.whenGetNew -= portDispl_whenGetNew;
m_portDispl.whenGetNew += new WhenGetNew(portDispl_whenGetNew);
m_portDispl.ConnectDeveice();
btnOpen.Text = "关闭串口";
}
else if (btnOpen.Text == "关闭串口")
{
m_portDispl.DisconnectDeveice();
btnOpen.Text = "打开串口";
}
}
///
///
{
WhenGetNew ehan = delegate
{
txtDisplay.AppendText(m_portDispl.dataSrc);
};
try
{
if (InvokeRequired)
{
this.Invoke(ehan);
}
}
catch
{
}
}
private void btnClear_Click(object sender, EventArgs e)
{
txtDisplay.Clear();
}
}
}
0人赞
分享
二维码
赏一个