Windows 带有 SerialPort 的表单 - 应用程序在关闭表单后挂起

Windows forms with SerialPort - app hangs after closing form

我有一个 windows 表单应用程序以连接到 Arduino 开发板。当我想关闭它时,它会保持打开状态,直到我停止调试模式。当我在 Visual Studio 中 运行ning 程序和我单独 运行 exe 文件时,会发生这种情况,我必须从任务管理器中停止它。

我尝试了 FormClosingFormClosed 事件,但结果是一样的。我唯一想到的是这个问题的发生是因为我在 SerialPortDataRecieved 事件中使用了许多 Invoke 函数来控制我的控件。我这样做是因为我需要对表单控件进行线程安全调用。这是我的代码的一部分:

private void spArduino_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
  if (spArduino.BytesToRead > 0)
  {
     string data = spArduino.ReadLine().Replace("\r", "");
     if (data.StartsWith("CUR_TEMP:"))
     {
         if (lbTemprature.InvokeRequired)
         {
            lbTemprature.Invoke(new MethodInvoker(delegate {
               lbTemprature.Text = "Room temprature  " + data.Remove(0,9) + "°C";
             }));
         }
     }
  }
}
///////
 private void Monitoring_FormClosing(object sender, FormClosingEventArgs e)
 {
     try
     {
          spArduino.WriteLine("CLEAR");
          spArduino.Close();
     }
     catch (Exception)
     {
          MessageBox.Show("errorclose");
     }
 }

这显示在我的输出中 (Visual Studio)

The thread 0x2bf0 has exited with code 0 (0x0).
The thread 0x5f24 has exited with code 0 (0x0).
The thread 0x46c4 has exited with code 0 (0x0).
The thread 0x5df4 has exited with code 0 (0x0).
The thread 0x294c has exited with code 0 (0x0).
The thread 0x4620 has exited with code 0 (0x0).
The thread 0x720 has exited with code 0 (0x0).
The thread 0x35a0 has exited with code 0 (0x0).

它一直保持这种状态,直到我停止程序。

任何人都可以帮助我了解我的问题在哪里以及我该如何解决?

查看您是如何清理串行设备的。尝试取消订阅活动(即 -= spArduino_DataReceived)。引用可能会阻止垃圾收集并在关闭后使您的表单保持活动状态。

SerialPort.DataReceived 事件在与主 UI 线程不同的线程上触发,这就是为什么在更新 UI 时需要调用 lbTemprature.Invoke 方法的原因。

关闭窗体而不关闭端口

如果不关闭端口就关闭窗体,那么在关闭窗体时,可能会在窗体处理后触发DataReceived事件,这会在尝试更新UI.

关闭窗体前关闭端口

如果您在关闭表单之前关闭端口(例如在 FormClosing 事件中),那么您可能会遇到死锁,因为主 UI 线程中的 SerialPort.Close()等待触发DataReceived事件的线程完成事件,DataReceived事件在你调用lbTemprature.Invoke时等待UI线程。这可能是导致表格冻结的原因。

解决方案

一个解决方案是调用lbTemprature.BeginInvoke来避免死锁。这可能还不够,因为 BeginInvoke 在处理表单后仍然可以 运行 并引起期望。可能需要向 Form.IsDisposed 属性 添加支票。

if (lbTemprature.InvokeRequired)
{
    lbTemprature.BeginInvoke(new MethodInvoker(delegate
    {
        if (!this.IsDisposed)
        {
            lbTemprature.Text = "Room temprature  " + data.Remove(0,9) + "°C";
        }
    }));
}