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