为什么InvokeRequired pferred超过WindowsFormsSynchronizationContext $ P $?InvokeRequired、pferred、WindowsFo

2023-09-05 01:49:19 作者:若不爱丶一切解释都是多余

每当初学者问类似:如何更新GUI ?从C#中的另一个线程,答案是pretty的直:

Anytime the beginner asks something like: How to update GUI from another thread in C#?, the answer is pretty straight:

if (foo.InvokeRequired)
{
    foo.BeginInvoke(...)
} else {
    ...
}

这是真的很好用吗?非GUI线程执行后右 foo.InvokeRequired 的状态可以改变。举例来说,如果我们在 foo.InvokeRequired 收的形式对的,但在此之前 foo.BeginInvoke ,要求 foo.BeginInvoke 将导致 InvalidOperationException异常:调用或BeginInvoke不能调用控件上,直到窗口句柄已创建。 的这不会发生,如果我们在调用之前关闭的形式 InvokeRequired ,因为这将是连从非GUI线程调用的时候。

But is it really good to use it? Right after non-GUI thread executes foo.InvokeRequired the state of foo can change. For example, if we close form right after foo.InvokeRequired, but before foo.BeginInvoke, calling foo.BeginInvoke will lead to InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created. This wouldn't happen if we close the form before calling InvokeRequired, because it would be false even when called from non-GUI thread.

另一个例子:比方说,文本框。如果关闭的形式,而且非GUI线程执行后 foo.InvokeRequired (这是假的,因为窗体被关闭)和 foo.AppendText 这将导致的ObjectDisposedException

Another example: Let's say foo is a TextBox. If you close form, and after that non-GUI thread executes foo.InvokeRequired (which is false, because form is closed) and foo.AppendText it will lead to ObjectDisposedException.

在此相反,在使用 WindowsFormsSynchronizationContext 我的意见是非常容易 - 使用帖子发布回调将出现只有当螺纹依然存在,并同步调用使用发送抛出 InvalidAsynchronousStateException 如果线程不存在了。

In contrast, in my opinion using WindowsFormsSynchronizationContext is much easier - posting callback by using Post will occur only if thread still exists, and synchronous calls using Send throws InvalidAsynchronousStateException if thread not exists anymore.

时没有使用 WindowsFormsSynchronizationContext 只是更容易?我失去了一些东西?为什么要使用InvokeRequired-的BeginInvoke模式,如果它没有真正线程安全的?你认为什么是更好的?

Isn't using WindowsFormsSynchronizationContext just easier? Am I missing something? Why should I use InvokeRequired-BeginInvoke pattern if it's not really thread safe? What do you think is better?

推荐答案

使用反射器,我们可以看到, WindowsFormsSynchronizationContext

Using Reflector we could see that WindowsFormsSynchronizationContext :

使用的BeginInvoke 发布无任何检查。无用本身只需要直接调用该函数。 使用调用只测试该线程是活在发送(丹尼尔·Hilgarth说的)。如果你有引用由另一个线程拥有的控制和活得比它,你已经有了一个问题,所以它不会改变任何东西,除了少数情况下,UI线程会活得比上创建控件辅助线程... Uses BeginInvoke for Post without any checks. Useless in itself just call the function directly. Uses Invoke testing only that the thread is alive on Send (As Daniel Hilgarth said). If you have a secondary thread that reference a control owned by another thread and outlive it you already got a problem so it doesn't change anything and except in a few cases the UI thread will outlive controls created on it...

确定正确的阅读它真的取决于你想要做的委托作为 WindowsFormsSynchronizationContext 基本上自身附加到只是为了这个目的而创建一个特殊的控制什么。因此,无论的BeginInvoke和调用会一直工作作为你的线程是活着的消息泵(由Application.Run启动)正在执行。

Ok reading correctly it really depends on what you want to do in the delegate as WindowsFormsSynchronizationContext basically attach itself to a special control created just for this purpose. So both BeginInvoke and Invoke will work as long as your thread is alive and the message pump (started by Application.Run) is executing.

所以,如果你使用 WindowsFormsSynchronizationContext 你可能在你调用对象的委托,它不具有任何处理,被放置很久以前的情况是(这样的的通话将失败与 InvalidOperationException异常其他)......它会出现,只要你访问什么,取决于手柄和崩溃后,当你改变了$ C $工作C(或当你的团队不太实验开发商不差):你应该早失败

So if you uses WindowsFormsSynchronizationContext you risk being in a case where you call a delegate on an object that doesn't have any handle and was disposed long time ago (Such call will have failed with InvalidOperationException otherwise)... It will appear to work as long as you access nothing that depends on the handle and crash later when you change the code (or worse when a less experimented developer on you team does) : You should fail early.

我的结论将是 WindowsFormsSynchronizationContext 应保留至您需要与另一个线程的消息泵进行交互,可能是由于一些方法线程亲和具体案件。对于简单的如何从一个计时器更新自己的状态情况下,它会误导新人,并带领他们到微妙的错误,不是一个好主意, InvokeRequired 真的是更适合于90 %用法。

My conclusion would be that WindowsFormsSynchronizationContext should be reserved to specific cases where you need to interact with a message pump on another thread, maybe due to some method thread-affinity. For simple "how do I update my form from a timer" cases it will mislead newcomers and lead them to subtle bugs, not a good idea, InvokeRequired is really more suited to 90% of usages.