.NET事件、线程和消息

.NET事件、线程和消息,.net,multithreading,events,.net,Multithreading,Events,我知道事件处理程序在调用事件的任何线程上执行。我进一步理解了只从创建控件的线程更新表单控件的必要性。我假设UI线程就是为了这个问题而创建表单的线程 如果事件是已发布消息(如绘制消息)的结果,那么处理程序不是与原始线程解耦吗?如果这是真的,那么任何线程都可以调用无效操作,并且生成的绘制将始终出现在UI线程上,因为它是处理表单消息的线程 这就是我在凌晨2点左右的时候脑子里浮现出来的情景,我身边有一个长长的空小吃碗。请澄清并更正,以便我能够正确理解工作中的机制。MSDN: 调用Invalidate方法

我知道事件处理程序在调用事件的任何线程上执行。我进一步理解了只从创建控件的线程更新表单控件的必要性。我假设UI线程就是为了这个问题而创建表单的线程

如果事件是已发布消息(如绘制消息)的结果,那么处理程序不是与原始线程解耦吗?如果这是真的,那么任何线程都可以调用无效操作,并且生成的绘制将始终出现在UI线程上,因为它是处理表单消息的线程

这就是我在凌晨2点左右的时候脑子里浮现出来的情景,我身边有一个长长的空小吃碗。请澄清并更正,以便我能够正确理解工作中的机制。

MSDN:

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


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

首先,请参阅Alex的答案

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

我在这里讲得很广泛;但请注意,任何代码都可以通过调用Control.Invoke()或等效函数在内部将内容封送到UI线程


当然,在您自己的应用程序代码中,您必须确保做到这一点。但我只是建议,如果您在某个地方发现代码似乎违反了这里的原则,那么它可能会在某个地方为您调用()。

即使从技术上讲可以这样做,您也会遇到麻烦。
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);
    }
}
这里有几个问题:

  • 如果在完成
    IsHandleCreated
    检查后处理
    表单
    ,则句柄将消失

  • 调用
    OnInvalidated
    NotifyInvalidate
    会触发
    Invalidated
    事件,将在调用线程上调用该事件。注册到该事件的处理程序可能不希望从其他线程调用它

  • 即使这说明可以从单独的线程调用
    invalidate
    ,但它并不说明
    RedrawWindow
    是否可以。因此,问题变成了您是调用
    Invalidate(false)
    (或
    Invalidate()
    ,这映射到
    false
    )还是调用
    Invalidate(true)


  • 长话短说。你不应该从不同的线程调用它。您应该通过
    Invoke
    BeginInvoke
    调用与
    控件
    交互的所有方法(也可以通过
    Invalidate
    调用
    Update
    后调用
    Invalidate
    与调用
    Refresh
    相同,这必然会从UI线程调用。