c# – 为什么InvokeRequired比WindowsFormsSynchronizationContext更受欢迎?

前端之家收集整理的这篇文章主要介绍了c# – 为什么InvokeRequired比WindowsFormsSynchronizationContext更受欢迎?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
任何时候初学者都会问: How to update the GUI from another thread in C#?,答案很简单:
if (foo.Invokerequired)
{
    foo.BeginInvoke(...)
} else {
    ...
}

但使用它真的很好吗?在非GUI线程执行foo.Invokerequired之后,foo的状态可以改变.例如,如果我们在foo.Invokerequired之后关闭表单,但在foo.BeginInvoke之前,调用foo.BeginInvoke将导致InvalidOperationException:在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke.如果我们在调用Invokerequired之前关闭表单,则不会发生这种情况,因为即使从非GUI线程调用它也会为false.

另一个例子:假设foo是一个TextBox.如果你关闭表单,之后非GUI线程执行foo.Invokerequired(这是假的,因为表单已关闭)和foo.AppendText它将导致ObjectDisposedException.

相反,在我看来,使用WindowsFormsSynchronizationContext要容易得多 – 只有当线程仍然存在时才会使用Post发布回调,而如果线程不再存在则使用Send的同步调用会抛出InvalidAsynchronousStateException.

是不是更容易使用WindowsFormsSynchronizationContext?我错过了什么吗?如果它不是真的线程安全,为什么我应该使用Invokerequired-BeginInvoke模式?你觉得哪个更好?

解决方法

WindowsFormsSynchronizationContext通过将自身附加到绑定到创建上下文的线程的特殊控件来工作.

所以

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

可以用更安全的版本替换:

context.Post(delegate
{
    if (foo.IsDisposed) return;
    ...
});

假设上下文是在foo所在的同一UI线程上创建的WindowsFormsSynchronizationContext.

此版本避免了您提出的问题:

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.

如果您使用多个消息循环或多个UI线程,请注意WindowsFormsSynchronizationContext.Post的一些特殊情况:

>只有在创建它的线程上仍有消息泵时,WindowsFormsSynchronizationContext.Post才会执行该委托.如果没有任何事情发生并且没有引发异常.如果稍后另一个消息泵被附加到线程(例如通过对Application.Run的第二次调用),则委托将执行(这是由于系统维护的事实)每个线程的消息队列,不知道有人从中抽取消息的事实)
>如果绑定的线程不再存在,WindowsFormsSynchronizationContext.Send将抛出InvalidAsynchronousStateException.但是如果它绑定的线程是活动的并且没有运行消息循环,它将不会立即执行但仍将被放置在消息队列中并在执行Application.Run时再次执行.

如果在自动处理的控件(如主窗体)上调用IsDisposed,则这些情况都不会意外执行代码,因为委托将立即退出,即使它是在意外时间执行的.

危险的情况是调用WindowsFormsSynchronizationContext.Send并考虑代码将被执行:它可能没有,现在有办法知道它是否做了什么.

我的结论是WindowsFormsSynchronizationContext是一个更好的解决方案,只要它被正确使用.

它可以在复杂的情况下创建sublte问题但是常见的GUI应用程序只有一个消息循环,只要应用程序本身一直很好.

猜你在找的C#相关文章