【问题标题】:How is this possible: OnPaint processed while in WaitOne这怎么可能:在 WaitOne 中处理 OnPaint
【发布时间】:2010-12-27 16:47:08
【问题描述】:

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

堆栈跟踪如下所示:

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

有人能解释一下这里发生了什么吗?

【问题讨论】:

    标签: c# .net winforms events


    【解决方案1】:

    这是设计使然。 CLR 遵守单线程单元 (STA) 的合同。 GUI 应用程序的主线程是 STA,这在 Windows 编程中是必需的,Main() 方法上的 [STAThread] 属性确保了这一点。

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

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

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

    【讨论】:

    • 你的答案总是一种洞察力,又学到了新东西。
    • 我正在等待的原因是因为我正在等待网络流量完成。服务器提供构建 UI 所需的数据。具体来说,我懒加载列表视图。当出现我没有数据的行时,我需要获取该数据才能对绘画做出反应。这是设计使然。我会看看Mutex 类是否有帮助。如果您有其他建议,我们当然欢迎。
    • 反之亦然。数据的到来可以引发绘画事件。 Control.BeginInvoke 和 Invalidate()。这在带有 asych/await 的 C# 5 中会容易得多。在此之前如有必要,请使用状态机。
    • 我创建了一个测试用例,其中两个线程中有两个表单。 Form1 我在 ManualResetEvent 上等待,Form2 有一个按钮来发出事件信号。我看到的是 WM_PAINT 事件正在发生,但我无法与被阻止的表单交互。此外,BeginInvoke 没有通过。你知道我怎样才能知道哪些消息会通过,哪些消息不会通过吗?
    • @jdv - 在 gui 应用程序中不使用 COM 是不可能的。当您键入 Ctrl+V 时,您正在使用 COM。 OpenFileDialog 是另一个大的,其中很多都带有 shell 扩展处理程序。这不是明确的 COM 互操作,而是 Windows 为您做的。它仍然需要一个 STA 线程。它是 Windows 工作方式的核心,CLR 必须支持它。 COM 处理线程的工作相当好,在 SO 上几乎没有关于它的问题。 .NET 线程也不能这么说,关于它的问题很多很多。然而,它更加更加灵活。价格不菲。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2014-09-25
      • 2013-10-15
      • 2011-01-31
      • 1970-01-01
      • 1970-01-01
      • 2020-05-25
      • 2018-11-06
      • 1970-01-01
      相关资源
      最近更新 更多