我曾经阅读一些论坛和一两个stackoverflow问题,说使用Delegate.BeginInvoke时必须使用Delegate.EndInvoke。 然而,许多关于使用BeginInvoke的文章都没有提到使用EndInvoke。 此外,我已经部署了仅使用BeginInvoke的生产代码,并且似乎没有任何内存问题。 我通常使用BeginInvoke处理那些我不在意它们何时完成或处理需要多长时间的线程。
我曾经阅读一些论坛和一两个stackoverflow问题,说使用Delegate.BeginInvoke时必须使用Delegate.EndInvoke。 然而,许多关于使用BeginInvoke的文章都没有提到使用EndInvoke。 此外,我已经部署了仅使用BeginInvoke的生产代码,并且似乎没有任何内存问题。 我通常使用BeginInvoke处理那些我不在意它们何时完成或处理需要多长时间的线程。
根据MSDN文章『将同步方法异步调用』:
无论使用哪种技术,都要调用EndInvoke来完成异步调用。
现在,有理论也有实践。像你之前的许多其他开发人员一样,你可能常常忽略文档要求,但实际上可以运行程序。不过问题在于:EndInvoke是否真正执行了防止应用程序崩溃、泄漏内存等必备操作,这可能是一个实现细节。但是有个事实需要注意,如果它是文档要求,那么你确实应该按照要求去做。这不仅是理论,也是为了在发生变化时保护自己。
通过记录下这个要求,异步调用机制的设计者基本上给自己留下了改变BeginInvoke和EndInvoke工作方式的自由,因此,如果有足够的原因(例如性能增强),EndInvoke可能会突然变得更加必要。 假设如果您忘记调用EndInvoke,它将突然导致死锁。他们已经通过说『总是调用EndInvoke』来覆盖自己;如果您的应用程序因为没有遵循此要求而停止工作,则责任在于您。
我并不是说这一定是一个可能发生的情况。我的观点只是你不应该或至少我不会以『如果可以省略,那就不必要』的心态去问:『这真的有必要吗?』,因为文档确实要求你这样做。
如果您计划从线程中抛出异常并且希望正确地捕获它们,那么调用EndInvoke是绝对必要的。如果您不调用EndInvoke,则抛出异常的线程将消失,并且您将无法得知任何信息。
为了支持EndInvoke,请提供一个AsyncCallback,然后在该回调方法中,请确保使用try/catch块包装对EndInvoke的调用。
虽然如果您不关心线程中发生的事情,可以不调用EndInvoke,但我认为只是调用EndInvoke是一个非常好的习惯。您永远不知道,某天初级开发人员可能会进入代码并抛出异常。然后更新的应用程序被部署,并开始调用服务。
GCHandle
实际上很容易创建内存泄漏。此外,问题通常不是内存泄漏,而是资源泄漏。如果某种逻辑打开操作(例如BeginInvoke
)告诉您应始终调用其逻辑关闭操作(例如EndInvoke
),那么我认为您应该这样做。 - Stephen ClearyMSDN指出,调用EndInvoke非常重要:
重要提示 无论使用哪种技术,都必须调用EndInvoke来完成异步调用。
已经有记录表明,在WinForms应用程序中使用BeginInvoke
在GUI线程上执行操作时,不需要EndInvoke
(假设您不等待IAsyncResult
)。
然而,这是一个特定的例外,适用于一般规则:对于每个BeginOperation
,必须有一个匹配的EndOperation
。如另一个A所述:如果GUI访问代码可能会抛出异常,则需要EndInvoke
来获取异常。
请参见此处(有点)官方确认:http://blogs.msdn.com/b/cbrumme/archive/2003/05/06/51385.aspx#51395(这是Chris Brummie的评论2003年5月12日下午5:50
)。
另外:关于Control.BeginInvoke
的文档中包含了以下备注:
如果需要,您可以调用
EndInvoke
来检索委托的返回值,但这不是必需的。EndInvoke
将会阻塞,直到返回值可以被检索。
所以,官方声明:使用异步委托在GUI线程上执行操作时,WinForm不要求使用EndInvoke
(除非您需要返回值或可能出现异常,在任何情况下都考虑使用Invoke
)。
Control
的 BeginInvoke
/EndInvoke
与 Delegate
的 BeginInvoke
/EndInvoke
完全不同。你不需要调用 Control.EndInvoke
,但这与异步委托无关。在 WinForms 应用程序中,你应该为每个异步委托调用 Delegate.EndInvoke
。 - Stephen ClearyControl.BeginInvoke
是唯一的例外。 - Richard我通常使用BeginInvoke的方式是处理那些我不关心何时完成或需要多长时间来处理的线程。
听起来你应该考虑使用Thread类而不是异步委托。
如果你关心结果或检测错误(这两者都需要将结果/错误转发到原始线程),那么你可以使用异步委托,在这种情况下,你需要EndInvoke。更好的方法是使用Task或Task<TResult>类。
如果你只想启动一些独立的操作,那么请使用Thread类。
来自Windows Form文档中的Control.BeginInvoke()
如果需要,您可以调用EndInvoke从委托中检索返回值,但这不是必需的。EndInvoke将阻塞,直到可以检索返回值。
这是关于UI线程上的Windows Form异步调用的特殊情况,这不适用于一般情况,但对于处于此情况的人可能会有所帮助。