c# - "Safe handle has been closed"带有 SerialPort 和 C# 中的线程

标签 c# multithreading exception serial-port stream

大家下午好!

我有这个线程化的 SerialPort 包装器,它从串口读取一行。这是我的线程代码。

protected void ReadData()
{
     SerialPort serialPort = null;
     try
     {
         serialPort = SetupSerialPort(_serialPortSettings);
         serialPort.Open();

         string data;
         while (serialPort.IsOpen)
         {
             try
             {

                 data = serialPort.ReadLine();
                 if (data.Length > 0)
                     ReceivedData(serialPort, new ReceivedDataEventArgs(data));

             }
             catch (TimeoutException)
             {
                 //  No action
             }
         }
     }
     catch (ThreadAbortException)
     {
         if (serialPort != null)
             serialPort.Close();
     }
}

当我调用 myThread.Abort(); 时出现异常(没有行或代码引用)“安全句柄已关闭”。谁能发现我做错了什么?谢谢。

顺便说一下,我有一个 Start() 和一个 Stop(),它们分别创建线程和中止线程。

最佳答案

我怀疑这是因为您正在使用 Thread.Abort 来结束线程——这通常是不受欢迎的。当您中止时线程的行为是不可预测的。因此,由于串行端口是 native 代码的包装器,因此存在 native 资源(由 .NET 中的 SafeHandle 表示)意外处理,因此您会得到异常。

你可以这样想你的线程会发生什么:

  • 你开始你的话题
  • 您打开串行端口(它分配 native 资源并使用 SafeHandle(s) 来保留这些资源)
  • 你开始从串口读取
  • 然后在某个时候(对您的线程来说出乎意料)您对其调用了 Thread.Abort
  • 此时您线程中的代码很可能正在尝试访问串口(以读取数据)
  • 线程被杀死,串口句柄被隐式销毁
  • 您从串口的 ReadLine() 函数内部的代码中抛出一个异常,因为它拥有的句柄不再有效

你真的应该使用不同的方法来中止线程,这样你就有机会关闭和处理串行端口。

关闭线程的正确方法可以使用像这样的 ManualResetEvent 来实现:

protected ManualResetEvent threadStop = new ManualResetEvent(false);

protected void ReadData()
{
     SerialPort serialPort = null;
     try
     {
         serialPort = SetupSerialPort(_serialPortSettings);
         serialPort.Open();

         string data;
         while (serialPort.IsOpen)
         {
             try
             {

                 data = serialPort.ReadLine();
                 if (data.Length > 0)
                     ReceivedData(serialPort, new ReceivedDataEventArgs(data));

             }
             catch (TimeoutException)
             {
                 //  No action
             }

             // WaitOne(0) tests whether the event was set and returns TRUE
             //   if it was set and FALSE otherwise.
             // The 0 tells the manual reset event to only check if it was set
             //   and return immediately, otherwise if the number is greater than
             //   0 it will wait for that many milliseconds for the event to be set
             //   and only then return - effectively blocking your thread for that
             //   period of time
             if (threadStop.WaitOne(0))
                 break;
         }
     }
     catch (Exception exc)
     {
         // you can do something here in case of an exception
         // but a ThreadAbortedException should't be thrown any more if you
         // stop using Thread.Abort and rely on the ManualResetEvent instead
     }
     finally
     {
         if (serialPort != null)
             serialPort.Close();
     }
}

protected void Stop()
{
    // Set the manual reset event to a "signaled" state --> will cause the
    //   WaitOne function to return TRUE
    threadStop.Set();
} 

当然,当使用事件方法停止线程时,您必须注意在所有长时间运行的循环或任务中包含事件状态检查。如果您不这样做,您的线程可能不会响应您设置的事件 - 直到它退出长时间运行的循环或任务并有机会“看到”事件已设置。

关于c# - "Safe handle has been closed"带有 SerialPort 和 C# 中的线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1319003/

相关文章:

c# - EF5 收到此错误消息 : Model compatibility cannot be checked because the database does not contain model metadata

c# - 在类库中实例化身份 ApplicationUserManager

Java套接字编程中的线程间通信问题

vb.net - 如何与多个线程共享一个资源(一个串口)

exception - PowerShell可以将错误输出自动转换为异常吗?

android - ImageView 导致 android.graphics.Canvas.throwIfRecycled 异常

java - 抛出 ExecutionException 但没有原因

c# - 使用 FlatBuffers 从 C# 序列化到 native 内存缓冲区

c# - “IApplicationBuilder”不包含 'MapAzureSignalR' 的定义

c# - 如何在 C# UWP 中暂停一个线程,同时继续其他线程