gpt4 book ai didi

c# - 为什么 InvokeRequired 优于 WindowsFormsSynchronizationContext?

转载 作者:太空狗 更新时间:2023-10-29 20:22:06 25 4
gpt4 key购买 nike

任何时候初学者都会问类似这样的问题: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 通过将自身附加到绑定(bind)到创建上下文的线程的特殊控件来工作。

所以

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

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

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

假设 context 是在与 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 仅当创建委托(delegate)的线程上仍有消息泵时才会执行委托(delegate)。如果没有什么都没有发生,也没有引发异常
    另外,如果另一个消息泵后来附加到线程(通过第二次调用 Application.Run 例如)委托(delegate)将执行(这是由于系统为每个线程维护一个消息队列,而不知道是否有人从中提取消息这一事实)
  • WindowsFormsSynchronizationContext.Send 将抛出 InvalidAsynchronousStateException 如果它绑定(bind)到的线程不再存在。但是如果它绑定(bind)的线程是事件的并且不运行消息循环它不会立即执行但仍然会被放置在消息队列中并在 Application.Run< 时执行 再次执行。

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

危险的情况是调用 WindowsFormsSynchronizationContext.Send 并考虑代码将被执行:它可能不会,现在有办法知道它是否做了任何事情。


我的结论是,只要使用得当,WindowsFormsSynchronizationContext 是更好的解决方案。

在复杂的情况下,它可能会产生一些微妙的问题,但具有一个消息循环的常见 GUI 应用程序只要应用程序本身就一直存在,就永远没问题。

关于c# - 为什么 InvokeRequired 优于 WindowsFormsSynchronizationContext?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5246023/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com