这怎么可能:在WaitOne中处理OnPaint

我有一个ManualResetEvent 。 有一次,我使用WaitOne等待该事件。 令我惊讶的是,我在WaitOne收到了一个OnPaint事件。 这种情况经常发生。

堆栈跟踪如下所示:

替代文字

我知道WaitOne会阻塞当前线程,并且在事件触发之前不允许执行任何其他代码。

有人可以解释这里发生了什么吗

这是设计的。 CLR尊重单线程公寓(STA)的合同。 GUI应用程序的主要线程是Windows编程中需要的STA,Main()方法上的[STAThread]属性确保了这一点。

STA线程的硬规则是它必须泵送消息循环(如Application.Run)并且永远不会阻塞。 当后台线程使用任何COM公寓线程对象时,阻止STA线程很可能导致死锁。 其中有很多,剪贴板和WebBrowser是.NET程序中常见的。 许多不太明显的,可用作.NET包装类。

CLR通过在使用lock语句或调用同步类的Wait方法时通过泵送消息循环来确保阻塞不会导致死锁。 或者Thread.Join()。 该消息循环调度WM_PAINT消息,导致Paint事件运行。

您需要重新构建程序以确保这不会导致问题。 非常重要的是要专注于不阻塞主线程。 当您拥有BackgroundWorker类或Control.BeginInvoke()时,很少需要它。 由于某种奇怪的原因,Mutex类不会进行这种抽运,这可能是另一种方式。 虽然如果你这样做,僵局就会潜伏在拐角处。

我也看到了lock()语句的这种行为。 显然.net框架Thread类在等待UI线程上的锁时启动消息循环。 这只是解释了发生了什么。 原因可能是在使用旧的STA COM对象时防止死锁。 我不知道有办法防止这种情况发生。