为什么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线程调用它也是false
。
另一个例子:假设 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; ... });
假设context
是在foo
所在的同一UI线程上创建的WindowsFormsSynchronizationContext
。
此版本避免了您提出的问题:
在非GUI线程执行foo.InvokeRequired之后,foo的状态可以改变。 例如,如果我们在foo.InvokeRequired之后关闭表单,但在foo.BeginInvoke之前,调用foo.BeginInvoke将导致InvalidOperationException:在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。 如果我们在调用InvokeRequired之前关闭表单,则不会发生这种情况,因为即使从非GUI线程调用它也是错误的。
如果您使用多个消息循环或多个UI线程,请注意WindowsFormsSynchronizationContext.Post
的一些特殊情况:
如果在自动处理的控件(如主窗体)上IsDisposed
则这些情况都不会意外执行代码,因为委托将立即退出,即使它是在意外时间执行的。
危险的情况是调用WindowsFormsSynchronizationContext.Send
并考虑代码将被执行:它可能没有,现在有办法知道它是否做了什么。
我的结论是WindowsFormsSynchronizationContext
是一个更好的解决方案,只要它被正确使用。
它可以在复杂的情况下创建sublte问题但是常见的GUI应用程序只有一个消息循环,只要应用程序本身一直很好。
谁说InvokeRequired
/ Control.BeginInvoke
是首选? 如果你问我,在大多数情况下,由于你提到的确切原因,这是一种反模式。 您链接的问题有很多答案,有些确实建议使用同步上下文(包括我的 )。
虽然任何给定的控件都可以在您尝试从发布的委托访问它时进行处理,但使用Control.IsDisposed
可以很容易地解决(因为您的委托在UI线程上执行,因此在运行时没有任何东西可以处理控件):
上述就是C#学习教程:为什么InvokeRequired比WindowsFormsSynchronizationContext更受欢迎?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
public partial class MyForm : Form { private readonly SynchronizationContext _context; public MyForm() { _context = SynchronizationContext.Current //... } private MethodOnOtherThread() { //... _context.Post(status => { // I think it's enough to check the form's IsDisposed // But if you want to be extra paranoid you can test someLabel.IsDisposed if (!IsDisposed) {someLabel.Text = newText;} },null); } }
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1042076.html