这怎么可能:在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对象时防止死锁。 我不知道有办法防止这种情况发生。