Warm tip: This article is reproduced from serverfault.com, please click

.net-挂在 NtUserPeekMessage 中;

(.net - Hang in NtUserPeekMessage; am I nuts in insisting this should not occur?)

发布于 2021-06-24 20:35:24

我收到了来自虚拟化环境中的故障​​的多个转储,其中应用程序只是挂断了说(没有响应)的 Windows。虚拟化环境的供应商认为这是一个应用程序问题,尽管客户无法在实际客户端机器上重现挂起但几乎可以在虚拟化环境中按需重复它。

小型转储文件显示相同的挂起;GUI 线程以 NtUserPeekMessage(通过 winforms Application.DoEvents)作为堆栈顶部停止。

应用程序代码如下所示(伪代码):

    while MsgWaitForMultipleObjects(1, new[]{eventhandle}, false, INFINITY, 0x4FF) != 0
        Application.DoEvents()   ;; HANG HERE

栈顶:

win32u.dll!NtUserPeekMessage() + 20 bytes
user32.dll!PeekMessageW() + 254 bytes
System.Windows.Forms.Ni.dll!(no symbol)
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.WIndows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessagLoop()
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner()
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop()
[Function given above]

Application.DoEvents 实际上并没有出现在堆栈上;看起来它的尾巴被叫出来了。

该应用程序是用 99.9% 的托管代码编写的,十多年来没有出现堆损坏错误。

我认为这永远不会发生是对的吗?

21 年 6 月 29 日的新信息(可能会使现有答案无效):

在没有 WS_CHILD 标志的窗口上使用 SetParent() 调用时,实际上并不附加输入队列。我能够在我自己的机器上观察到 AttachThreadInput() 在尝试删除不存在的附件时实际上会产生错误;但是在现场没有观察到错误,这意味着某些东西正在附加不应该的输入队列。

Questioner
Joshua
Viewed
11
Ben Voigt 2021-06-25 06:44:02

根据包括非常值得信赖的 Raymond Chen 的 Old New Thing 在内的多个在线消息来源,当多个线程附加到同一个输入队列时,所有消息队列功能都会成为阻塞同步操作。

在这里展示:

该开发人员进行了调试工作,发现此确切堆栈跟踪上的挂起是由附加的输入队列引起的。

我推测PeekMessage需要为共享输入队列获取互斥锁......当前由另一个线程持有的互斥锁。同步对象的争用会导致响应速度很差,直至彻底死锁。要进一步调试,你必须查看共享同一输入队列的其他线程正在做什么。