使用SerialPort和C#中的线程"关闭了安全句柄"

Dan*_*ite 7 c# multithreading serial-port exception 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();
     }
}
Run Code Online (Sandbox Code Playgroud)

当我打电话给myThread.Abort();我得到一个例外(没有行或代码参考)"安全句柄已关闭".谁能发现我做错了什么?谢谢.

顺便说一句,我有一个Start()和一个Stop()创建线程并恭敬地中止线程.

Mik*_*scu 10

我怀疑这是因为你使用Thread.Abort来结束线程 - 这通常是不赞成的.中止它时的线程行为是不可预测的.因此,由于串行端口是本机代码的包装器,因此存在本机资源(由.NET中的SafeHandle表示),这些资源会被意外处理掉,因此您将获得异常.

您可以考虑这样的线程会发生什么:

  • 你开始你的线程
  • 打开串口(分配本机资源并使用SafeHandle来保留这些资源)
  • 你开始从串口读取
  • 然后在某些时候(你的线程意外),你调用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();
} 
Run Code Online (Sandbox Code Playgroud)

当然,当使用events方法来停止线程时,您必须小心地在所有长时间运行的循环或任务中包含事件状态检查.如果你不这样做,你的线程似乎不会响应你的设置事件 - 直到它退出长时间运行的循环,或任务并有机会"看到"事件已被设置.