首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SerialPort.GetPortNames()导致ContextSwitchDeadlock异常,为什么?

SerialPort.GetPortNames()导致ContextSwitchDeadlock异常,为什么?
EN

Stack Overflow用户
提问于 2019-08-01 11:14:26
回答 1查看 157关注 0票数 1

我正在开发一个多线程串口通信软件,我注意到在使用下面提供的代码时抛出了一个ContextSwitchDeadlock

注意,将GetPortNames()追加到自己的函数中,然后将每个线程的in (开始和结束)写入控制台,就会显示每个已启动的线程也是正确的,如显式示例所示。

简单的例子:

代码语言:javascript
复制
while (true)
{
    Task.Run(() => SerialPort.GetPortNames());
    Thread.Sleep(100);
}

明确的例子:

代码语言:javascript
复制
[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);
}

字符串比较示例(无例外):

代码语言:javascript
复制
[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),并在长时间运行操作中常规地泵出消息。

我很好奇为什么会发生这种事。

提供的简单示例显示了实现这一目标的最简单方法,而显式示例显示了跟踪所有线程的方法,其输出如下:

代码语言:javascript
复制
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号线程在哪里)。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-08-01 12:03:13

主线程上有一个[STAThread]属性。

As this answer explains,因此,您的线程需要泵出一个COM消息队列。但是,您的主线程没有访问消息队列的权限(因为它只是在一个简单的控制台应用程序中),而且它被困在一个无限循环中,所以无论如何它都不会抽出任何东西!

控制台应用程序使用[STAThread]是非常不寻常的。如果您没有使用COM显式地执行任何操作(即没有添加它的特定原因),那么您可能可以安全地删除它。如果您确实出于某种原因需要使用[STAThread],那么您需要重新考虑一下您的线程模型。

至于为什么ContextSwitchException只在调用SerialPort时才会发生,我猜SerialPort是在调用winapi,这最终会调用一些检查线程是否正在释放消息队列的东西。如果您有兴趣的话,可以使用调试器进一步研究这个问题。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57307774

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档