当前位置: 首页 > 编程语言 > C#

为什么InvokeRequired比WindowsFormsSynchronizationContext更受欢迎?分享

时间:2023-04-10 17:29:19 C#

为什么InvokeRequired比WindowsFormsSynchronizationContext更受欢迎?任何时候初学者都会问:如何从C#中的另一个线程更新GUI?,答案很简单:if(foo.InvokeRequired){foo.BeginInvoke(...)}else{...}但是真的好用吗?通过在非GUI线程上执行foo.InvokeRequired,foo的状态可以改变。例如,如果我们在foo.InvokeRequired之后但在foo.BeginInvoke之前关闭窗体,则调用foo.BeginInvoke将导致InvalidOperationException:在创建窗口句柄之前无法在控件上调用Invoke或BeginInvoke。如果我们在调用InvokeRequired之前关闭表单,则不会发生这种情况,因为即使从非GUI线程调用它也是错误的。另一个例子:假设foo是一个TextBox。如果关闭表单,之后非GUI线程将执行foo.InvokeRequired(这是错误的,因为表单已关闭)和foo.AppendText,这将导致ObjectDisposedException。相反,在我看来,使用WindowsFormsSynchronizationContext更容易-仅当线程仍然存在时才使用Post回调,如果线程不再存在,则Send与Send同步调用并抛出InvalidAsynchronousStateException。使用WindowsFormsSynchronizationContext不是更容易吗?我错过了什么吗?如果InvokeRequired-BeginInvoke模式不是真正的线程安全,我为什么要使用它?你认为哪一个更好?WindowsFormsSynchronizationContext通过将自身附加到绑定到创建上下文的线程的特殊控件来工作。所以if(foo.InvokeRequired){foo.BeginInvoke(...)}else{...}可以替换为更安全的版本:context.Post(delegate{if(foo.IsDisposed)return;...});假设上下文是在与foo相同的UI线程上创建的WindowsFormsSynchronizationContext。这个版本避免了你提出的问题:foo的状态可以在非GUI线程执行foo.InvokeRequired后改变。例如,如果我们在foo.InvokeRequired之后但在foo.BeginInvoke之前关闭窗体,则调用foo.BeginInvoke将导致InvalidOperationException:在创建窗口句柄之前无法在控件上调用Invoke或BeginInvoke。如果我们在调用InvokeRequired之前关闭表单,则不会发生这种情况,因为即使从非GUI线程调用它也是错误的。如果您使用多个消息循环或多个UI线程,请注意WindowsFormsSynchronizationContext.Post的一些特殊情况:如果IsDisposed在自动处理的控件(例如主窗体)上,这些情况都不会意外执行代码,因为委托将退出立即执行,即使它是在意外的时间执行的。危险的情况是调用WindowsFormsSynchronizationContext.Send并考虑代码将被执行:它可能不会,现在有一种方法可以知道它是否做了什么。我的结论是,WindowsFormsSynchronizationContext只要使用得当,是一个更好的解决方案。它可以在复杂的情况下产生微妙的问题,但普通的GUI应用程序只有一个消息循环,只要应用程序本身没有问题。谁说InvokeRequired/Control.BeginInvoke是首选?如果您问我,在大多数情况下,出于您提到的确切原因,这是一种反模式。您链接的问题有很多答案,有些确实建议使用同步上下文(包括我的)。当您尝试从已发布的委托访问任何给定的控件时,可以处置它,这可以使用Control.IsDisposed轻松解决(因为您的委托在UI线程上执行,所以在运行时没有任何内容可以处理控件):C#学习教程:为什么InvokeRequired比WindowsFormsSynchronizationContext更受欢迎?如果分享的内容对你有用,需要进一步了解C#学习教程,希望你多多关注——publicpartialclassMyForm:Form{privatereadonlySynchronizationContext_context;publicMyForm(){_context=SynchronizationContext.Current//...}privateMethodOnOtherThread(){//..._context.Post(status=>{//我认为检查表单的IsDisposed就足够了//但是如果你想要更加偏执,你可以测试someLabel.IsDisposedif(!IsDisposed){someLabel.Text=newText;}},null);}}本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: