大家下午好!
我有这个线程化的 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/