为什么在跨线程UI组件调用中没有EndInvoke?
我一直在尝试将一些字符串添加到我的应用程序(简单的winform)上的一些ListBox中 – 我在使用BeginInboke时这样做了
myListBox.BeginInvoke(new Action(delegate() { myListBox.Items.Add( "some string" )); }));
在我再次阅读这3行之后 – 我不明白为什么在我在谷歌和MSDN上看到的任何跨线程UI的例子中我都没有看到任何EndInvoke的调用? 有没有理由不在这种情况下调用EndInvoke?
这是.NET中一个不幸的命名选择。 Control.BeginInvoke和Dispatcher.BeginInvoke方法与委托的方法具有相同的名称,但操作完全不同。 主要区别:
-
委托的BeginInvoke()方法始终是类型安全的,它具有与委托声明完全相同的参数。 Control / Dispatcher版本完全缺少这一点,参数通过类型为object []的params数组传递。 当你得到一个错误的参数时,编译器不会告诉你,它会在运行时发生爆炸
-
委托的Invoke()方法在同一个线程上运行委托目标。 与Control / Dispatcher.Invoke()不同,它们封送对UI线程的调用
-
捕获委托的BeginInvoke()目标中引发的exception,不会导致程序失败。 在调用EndInvoke()时重新抛出。 对于Control / Dispatcher.BeginInvoke()来说,情况并非如此,它们会在UI线程上引发exception。 没有合适的方法来捕获exception,Application.UnhandledException存在的一个更大的原因。
-
需要调用委托的EndInvoke()方法,如果不这样做,则会导致10分钟的资源泄漏。 Control / Dispatcher.BeginInvoke()方法不需要它,在实践中你永远不会这样做。
-
使用Control / Dispatcher.Invoke()是有风险的,它很容易导致死锁。 当UI线程未准备好调用目标时触发,并执行某些不明智的操作,如等待线程完成。 对于委托来说不是问题,至少因为它的Invoke()方法不使用线程。
-
在UI线程上调用Control / Dispatcher.BeginInvoke()是受支持的方案。 正如预期的那样,目标仍然在UI线程上运行。 但是后来,在UI线程再次空闲并重新进入调度程序循环之后。 这实际上是一个非常有用的function,它有助于解决棘手的重入问题。 特别是在用于UI控件的事件处理程序中,当您运行具有太多副作用的代码时,这些控件将会出错。
一个包含大量实施细节的大清单。 TLDR版本当然是:“它们没有任何共同之处,没有调用EndInvoke很好而且完全正常”。
Control.BeginInvoke
似乎并不完全遵循通常的BeginX
/ EndX
模式,即异步编程模型(APM) 。 通常,您必须为每个BeginX
调用EndX
,但在Control.BeginInvoke
的情况下,这不是严格要求的:
“如果需要, 可以调用
EndInvoke
从委托中检索返回值,但这不是必需的 .EndInvoke将阻塞,直到可以检索返回值。”– 来自
Control.BeginInvoke
的MSDN参考页面上的备注部分(由我强调)
在实践中,几乎没有必要。 这是因为通常调用该方法让一些代码在更新UI的UI线程上执行。 更新UI通常不会产生任何返回值,因此您不希望调用EndInvoke
。
- 在C#中的服务器上使用StreamWriter
- 在c#中使用WinSCard API读/写SLE4442存储卡
- 测试对象是否为任何generics类型实现通用接口
- 如何打开Internet Explorer窗口,在URL上导航并使用c#和mshtml库获取她的文档(HTMLDocument或InternetExplorer)
- 使用IOCTL_DVD_ *控制代码从C#调用DeviceIoControl
- 为什么我的WSDL仍然显示基本的http绑定与http的位置值?
- (新的Firebase Unity SDK)在查询上调用GetValueAsync不会在第一次调用时触发其ContinueWith
- 我应该避免在变量名中使用unicode字符吗?
- 是否保证线程将在aspnet中完成?