C# Windows服务串口通信运行30分钟后异常停止求助
Windows服务串口处理30分钟后停止响应问题排查与解决
我开发了一个Windows服务,功能是从串口读取来电号码并返回数据库数据,运行初期完全符合预期,但精确运行30分钟后会彻底停止处理数据。服务进程仍保持运行状态,但不再执行串口的读写操作,且串口错误接收事件从未触发。相关代码如下:
protected override void OnStart(string[] args) { string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "Started: " + Convert.ToString(DateTime.Now) + Environment.NewLine); File.AppendAllText(fullpath, "Machine Name:" + Environment.MachineName + Environment.NewLine); var worker = new Thread(mainevent); worker.IsBackground = false; worker.Start(); } private void mainevent() { SerialPort MyCOMPort = new SerialPort(); string COM_PortName = "COM2"; int BaudRate = 9600; MyCOMPort.PortName = COM_PortName; MyCOMPort.BaudRate = BaudRate; MyCOMPort.Parity = Parity.None; MyCOMPort.DataBits = 8; MyCOMPort.StopBits = StopBits.One; MyCOMPort.Handshake = Handshake.None; MyCOMPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); MyCOMPort.ErrorReceived += new SerialErrorReceivedEventHandler(ErrorReceivedHandler); MyCOMPort.RtsEnable = true; MyCOMPort.DtrEnable = true; MyCOMPort.Open(); } private void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e) { SerialPort sp = (SerialPort)sender; string fullpath = "C:/DEBUG/debut.txt"; File.AppendAllText(fullpath, "WE HAVE RECEIEVED AN ERROR"); } private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { try { SerialPort sp = (SerialPort)sender; string fullpath = "C:/debug/debug.txt"; var ak = String.Format("{0}", ACK); var nk = String.Format("{0}", NAK); var sx = String.Format("{0}", STX); var ex = String.Format("{0}", ETX); var crt = String.Format("{0}", CR); var lfd = String.Format("{0}", LF); string indata = sp.ReadExisting(); string prxData = indata.Normalize(); string rxData = prxData.Trim(); File.AppendAllText(fullpath, "rx:" + indata + "-" + Convert.ToString(DateTime.Now) + Environment.NewLine); if (rxData.Equals("H")) { sp.Write(Convert.ToString(ak)); } if (char.IsDigit(indata[0]) && firstpart.Length < 1) { firstpart = indata; } else if (char.IsDigit(indata[0])) { firstpart = firstpart + indata; string nena = getNena(firstpart); byte[] nenaascii = System.Text.Encoding.ASCII.GetBytes(nena); File.AppendAllText(fullpath, "Phone Number:" + firstpart.Substring(0, 10) + Environment.NewLine); File.AppendAllText(fullpath, "Record Number: 2" + firstpart.Substring(10, 2) + Environment.NewLine); sp.Write(Convert.ToString(ak)); sp.Write(Convert.ToString(sx)); sp.Write(nenaascii, 0, nena.Length); sp.Write(Convert.ToString(crt)); sp.Write(Convert.ToString(lfd)); sp.Write(ex); firstpart = ""; sp.DiscardInBuffer(); sp.DiscardOutBuffer(); } } catch (Exception ex) { string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "ERROR:" + ex.ToString() + Environment.NewLine); } }
问题原因分析
- SerialPort对象被垃圾回收:
mainevent方法中创建的MyCOMPort是局部变量,方法执行完毕后失去强引用,可能被GC回收,导致事件订阅失效,无法响应串口数据。 - 工作线程过早退出:
mainevent仅完成串口初始化和打开就执行完毕,线程随之退出。虽然SerialPort会在后台触发事件,但无持续强引用时对象易被回收。 - 错误日志路径错误:
ErrorReceivedHandler中日志路径为C:/DEBUG/debut.txt,与其他日志路径不一致,即使触发错误也无法正常记录。 - 缺失超时与连接保持机制:未设置串口读写超时,部分设备/驱动在30分钟无操作后可能进入休眠状态,导致数据接收中断。
解决方案
- 保持SerialPort强引用:将
MyCOMPort声明为类成员变量,确保服务生命周期内持有引用,避免被GC回收。 - 阻塞工作线程:在
mainevent中添加Thread.Sleep(Timeout.Infinite),防止线程过早退出。 - 修正错误日志路径:统一日志文件路径,确保错误信息能被记录。
- 添加串口超时设置:设置
ReadTimeout和WriteTimeout,避免长时间阻塞,若设备支持可定期发送心跳包保持连接。 - 优化数据处理逻辑:避免在
DataReceivedHandler中执行耗时操作(如数据库查询),可将数据放入队列由单独线程处理,防止阻塞事件线程。
修改后的示例代码
// 类成员变量,保持SerialPort强引用 private SerialPort _myCOMPort; private string firstpart = string.Empty; protected override void OnStart(string[] args) { string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "Started: " + DateTime.Now + Environment.NewLine); File.AppendAllText(fullpath, "Machine Name:" + Environment.MachineName + Environment.NewLine); var worker = new Thread(MainEvent); worker.IsBackground = false; worker.Start(); } private void MainEvent() { try { _myCOMPort = new SerialPort(); string COM_PortName = "COM2"; int BaudRate = 9600; _myCOMPort.PortName = COM_PortName; _myCOMPort.BaudRate = BaudRate; _myCOMPort.Parity = Parity.None; _myCOMPort.DataBits = 8; _myCOMPort.StopBits = StopBits.One; _myCOMPort.Handshake = Handshake.None; // 添加读写超时设置 _myCOMPort.ReadTimeout = 5000; _myCOMPort.WriteTimeout = 5000; _myCOMPort.DataReceived += DataReceivedHandler; _myCOMPort.ErrorReceived += ErrorReceivedHandler; _myCOMPort.RtsEnable = true; _myCOMPort.DtrEnable = true; _myCOMPort.Open(); string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "Serial port opened successfully: " + DateTime.Now + Environment.NewLine); // 阻塞线程,防止退出 Thread.Sleep(Timeout.Infinite); } catch (Exception ex) { string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "Serial port initialization error: " + ex.ToString() + Environment.NewLine); } } private void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e) { SerialPort sp = (SerialPort)sender; string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "ERROR RECEIVED: " + e.EventType + " at " + DateTime.Now + Environment.NewLine); } private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { try { SerialPort sp = (SerialPort)sender; string fullpath = "C:/debug/debug.txt"; var ak = Convert.ToString(ACK); var sx = Convert.ToString(STX); var ex = Convert.ToString(ETX); var crt = Convert.ToString(CR); var lfd = Convert.ToString(LF); string indata = sp.ReadExisting(); string prxData = indata.Normalize(); string rxData = prxData.Trim(); File.AppendAllText(fullpath, "rx:" + indata + "-" + DateTime.Now + Environment.NewLine); if (rxData.Equals("H")) { sp.Write(ak); } if (!string.IsNullOrEmpty(indata) && char.IsDigit(indata[0])) { if (string.IsNullOrEmpty(firstpart)) { firstpart = indata; } else { firstpart += indata; // 验证长度,避免Substring越界 if (firstpart.Length >= 12) { string phoneNumber = firstpart.Substring(0, 10); string recordNumber = "2" + firstpart.Substring(10, 2); File.AppendAllText(fullpath, "Phone Number:" + phoneNumber + Environment.NewLine); File.AppendAllText(fullpath, "Record Number: " + recordNumber + Environment.NewLine); string nena = getNena(firstpart); byte[] nenaascii = System.Text.Encoding.ASCII.GetBytes(nena); sp.Write(ak); sp.Write(sx); sp.Write(nenaascii, 0, nena.Length); sp.Write(crt); sp.Write(lfd); sp.Write(ex); firstpart = ""; sp.DiscardInBuffer(); sp.DiscardOutBuffer(); } } } } catch (Exception ex) { string fullpath = "C:/debug/debug.txt"; File.AppendAllText(fullpath, "Data handling ERROR:" + ex.ToString() + " at " + DateTime.Now + Environment.NewLine); } } // 假设getNena是你的数据库查询方法 private string getNena(string phoneNumber) { // 数据库查询逻辑 return string.Empty; }
内容的提问来源于stack exchange,提问作者jkeefe56
相关产品推荐
相关产品推荐

