【发布时间】:2019-08-01 11:14:26
【问题描述】:
我正在开发一个多线程串行端口通信软件,我注意到在使用下面提供的代码时会抛出 ContextSwitchDeadlock。
请注意,将GetPortNames() 附加到它自己的函数中,然后将每个线程的 ID(开始和结束)写入控制台表明每个启动的线程也是正确的,如显式示例所示。
简单示例:
while (true)
{
Task.Run(() => SerialPort.GetPortNames());
Thread.Sleep(100);
}
显式示例:
[STAThread]
private static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
Task.Run(() => Example());
Thread.Sleep(100);
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
}
private static void Example()
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
SerialPort.GetPortNames();
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
字符串比较示例(无例外):
[STAThread]
private static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
Task.Run(() => Example());
Thread.Sleep(100);
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
}
private static void Example()
{
Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
string.Compare("a", "b");
Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}
由于我认为没有任何线程被死锁,因此不应发生此错误。完整的错误信息:
托管调试助手 'ContextSwitchDeadlock' : 'CLR 在 60 秒内无法从 COM 上下文 0xb434c0 转换到 COM 上下文 0xb43408。拥有目标上下文/单元的线程很可能要么进行非泵送等待,要么处理非常长时间运行的操作而不泵送 Windows 消息。这种情况通常会对性能产生负面影响,甚至可能导致应用程序变得无响应或内存使用量随着时间的推移不断累积。为避免此问题,所有单线程单元 (STA) 线程都应使用泵送等待原语(例如 CoWaitForMultipleHandles)并在长时间运行的操作期间定期泵送消息。
我很想知道为什么会这样。
提供的简单示例显示了实现此目的的最简单方法,而显式示例显示了跟踪所有线程的方法,其输出为:
Start 1
Start 3
End 3
End 1
Start 1
Start 3
End 3
End 1
Start 1
Start 4
End 4
End 1
Start 1
Start 3
End 3
End 1
因此可以看出没有线程死锁,因为它们都正确开始和结束(虽然我不知道 2 号线程在哪里)。
【问题讨论】:
-
您的入口点标记为
[STAThread]。为什么?这是控制台应用程序还是 GUI 应用程序? -
静态方法通常是线程安全的,我看不出source有什么特别之处,所以和串口无关。
-
我认为这是你的主线程。 STA 线程应该会泵送消息队列,但您的线程没有这样做,因为 1)它是一个无法访问消息队列的控制台应用程序,以及 2)它正在执行无限循环。
-
@canton7 说得通。我启用了 ContextSwitchException,但它没有给我太多信息,因为它在“应用程序处于中断模式”页面上停止。如果您将评论作为答案发布,我可以选择它作为帖子的正确答案,以便其他人可以更快地看到答案。
标签: c# multithreading serial-port task