【问题标题】:.NET events, threads, and messages.NET 事件、线程和消息
【发布时间】:2010-10-30 05:49:46
【问题描述】:

我了解事件处理程序在调用该事件的任何线程上执行。我进一步了解仅从创建控件的线程更新表单控件的需要。我假设 UI 线程是为此问题创建表单的线程。

如果事件是发布消息(例如绘制消息)的结果,那么处理程序是否与原始线程分离?如果这是真的,那么任何线程都可以调用失效操作,并且生成的绘制将始终发生在 UI 线程上,因为它是处理表单消息的一个。

这就是它在凌晨 2:00 左右在我脑海中的映射方式,我身边放着一个长空的零食碗。请澄清和更正,以便我可以正确理解工作机制。

【问题讨论】:

    标签: .net multithreading events


    【解决方案1】:

    MSDN:

    调用 Invalidate 方法不会强制同步绘制;要强制同步绘制,请在调用 Invalidate 方法后调用 Update 方法。

    因此,Invalidate 可以从任何线程调用,而 Update 只能从 UI 线程调用。在任何情况下,要 100% 确保不使用无效的跨线程调用,请在程序开始时将 Control::CheckForIllegalCrossThreadCalls 属性设置为 true。这会导致任何无效调用立即失败,您无需猜测。

    【讨论】:

    • Invalidate 之后调用Update 与调用Refresh 的效果相同,这将导致必须从UI 线程调用。
    【解决方案2】:

    首先,看看 Alex 的回答。

    其次,请注意,仅仅因为您所做的某事导致某事最终出现在消息泵中,从而导致 UI 线程上的事件,并不绝对意味着启动操作必须在同一个线程上发生。

    我在这里说的很笼统;但请注意,任何代码都可以通过调用 Control.Invoke() 或等效的方法在内部将事物编组到 UI 线程。

    当然,在您自己的应用程序代码中,您必须确保这样做。但我只是建议,如果你在某处发现代码似乎违反了这里的原则,那么它可能是 Invoke() 适合你的地方。

    【讨论】:

      【解决方案3】:

      即使在技术上可以做到这一点,您也会遇到麻烦。 Invalidate 的代码如下:

      public void Invalidate(bool invalidateChildren) {
          if (IsHandleCreated) {
              if (invalidateChildren) {
                  SafeNativeMethods.RedrawWindow(new HandleRef(window, Handle),
                                                  null, NativeMethods.NullHandleRef,
                                                  NativeMethods.RDW_INVALIDATE |
                                                  NativeMethods.RDW_ERASE |
                                                  NativeMethods.RDW_ALLCHILDREN);
              }
              else {
                  // It's safe to invoke InvalidateRect from a separate thread.
                  using (new MultithreadSafeCallScope())
                  {
                      SafeNativeMethods.InvalidateRect(new HandleRef(window, Handle),
                                                      null,
                                                      (controlStyle & ControlStyles.Opaque) != ControlStyles.Opaque);
                  }
              }
      
              NotifyInvalidate(this.ClientRectangle);
          }
      }
      

      这里有几个问题:

      1. 如果FormIsHandleCreated检查完成后被释放,句柄将消失;

      2. 调用OnInvalidatedNotifyInvalidate触发Invalidated事件将在调用线程上调用。注册到该事件的处理程序可能不希望它是从不同的线程调用的;

      3. 尽管这表明InvalidateRect 可以从单独的线程中调用,但它并没有说明RedrawWindow 是否可以。因此问题就变成了您是调用Invalidate(false)(或Invalidate(),这映射到false)还是调用Invalidate(true)

      长话短说。你不应该从不同的线程调用它。您应该通过InvokeBeginInvoke 调用与Control(也包括Form)交互的所有方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-25
        • 1970-01-01
        • 1970-01-01
        • 2011-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多