加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 为什么InvokeRequired比WindowsFormsSynchronizationConte

发布时间:2020-12-15 08:07:20 所属栏目:百科 来源:网络整理
导读:任何时候初学者都会问: How to update the GUI from another thread in C#?,答案很简单: if (foo.InvokeRequired){ foo.BeginInvoke(...)} else { ...} 但使用它真的很好吗?在非GUI线程执行foo.InvokeRequired之后,foo的状态可以改变.例如,如果我们在foo.
任何时候初学者都会问: 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应用程序只有一个消息循环,只要应用程序本身一直很好.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读