25

我已经阅读了几个论坛,甚至一两个 stackoverflow 问题都说在使用 Delegate.BeginInvoke 时必须使用 Delegate.EndInvoke。我读过的很多关于使用 BeginInvoke 的文章都没有提到使用 EndInvoke。此外,我仅使用 BeginInvoke 部署了生产代码,似乎没有任何内存问题。我使用 BeginInvoke 的方式通常是使用我不关心它们何时完成或它们需要多长时间来处理的线程。

4

7 回答 7

45

来自 MSDN 文章“异步调用同步方法”

无论您使用哪种技术,始终调用 EndInvoke 来完成您的异步调用。

现在,有理论,然后有实践。您已经发现,就像您之前的许多其他开发人员一样,您通常可以忽略这个记录在案的要求而侥幸成功。它可能是一个实现细节,是否EndInvoke真的做了任何绝对必要的事情来防止你的应用程序崩溃、内存泄漏等。但事情是这样的:如果这是一个记录的要求,你真的应该这样做。这不仅仅是理论。这是关于在发生变化时保护自己。

通过记录这个要求,这个异步调用机制的设计者基本上给了自己改变方式BeginInvokeEndInvoke工作的自由,这样,如果有足够的理由(例如,性能增强),EndInvoke可能突然变得更加必要。假设如果你忘记它会突然导致死锁。他们已经用“总是打电话EndInvoke”来掩饰自己;如果您的应用程序因您未遵循此要求而停止运行,那么您有责任。

我并不是说这必然是一种可能的情况。我的观点很简单,你不应该——或者至少我不会——问“这真的有必要吗?” 怀着这样的心态,如果我能摆脱它,那么我会的,因为记录表明你应该这样做。

于 2011-01-03T14:21:56.103 回答
14

如果您计划从线程中抛出异常并期望正确捕获它们,那么这是绝对必要的。如果您不调用 EndInvoke,则引发异常的线程将消失,您将对此一无所知。

要支持 EndInvoke,请提供 AsyncCallback,并在该回调方法中,确保使用 try/catch 块包装对 EndInvoke 的调用。

如果你不关心线程中发生的事情,你可以不这样做,但我认为这是一个非常好的习惯,只需调用 EndInvoke。你永远不会知道,有朝一日,初级开发人员可能会进入那里并更改线程中的代码,然后抛出异常。然后更新的应用程序被部署,服务调用开始进入。

于 2011-01-03T14:10:52.177 回答
9

我听说了可能出现的内存泄漏问题。

通过关键字搜索,我发现了一个很好的讨论。

Does not calling EndInvoke *really* cause a memory leak ?

It can but it won't necessarily. Technically there is no such thing as a memory leak in .NET. Eventually the memory will be reclaimed by the GC. The problem is that it might be around a long time. The reason that you should call EndInvoke is because the results of the invocation (even if there is no return value) must be cached by .NET until EndInvoke is called. For example if the invoked code throws an exception then the exception is cached in the invocation data. Until you call EndInvoke it remains in memory. After you call EndInvoke the memory can be released.

这是参考

另一个参考

于 2011-01-03T14:12:04.047 回答
1

MSDN 告诉我们,调用 EndInvoke 很重要:

重要说明无论您使用哪种技术,始终调用 EndInvoke 来完成您的异步调用。

于 2011-01-03T14:11:08.117 回答
1

已经记录了在 WinForms 应用程序中使用 GUI 线程执行操作时EndInvoke不需要(没有分配非托管资源 - 假设您不等待IAsyncResult) 。BeginInvoke

然而,这是一般规则的一个特定例外:对于每个必须有一个匹配的. 正如此处另一个 A 所述:如果 GUI 访问代码可以抛出,则需要获取异常。BeginOperationEndOperationEndInvoke

请参阅此处以获取(某种)官方确认: http: //blogs.msdn.com/b/cbrumme/archive/2003/05/06/51385.aspx#51395(这是来自 Chris Brummie 的评论12 May 2003 5:50pm

附加:文档Control.BeginInvoke包括此注释:

如果需要,您可以调用EndInvoke以从委托中检索返回值,但这不是必需的。EndInvoke将阻塞,直到可以检索到返回值。

所以它是官方的:使用 WinForm 的使用异步委托在 GUI 线程上执行操作不需要 EndInvoke(除非您需要返回值或可能的异常,在任何一种情况下都考虑使用Invoke)。

于 2011-01-03T14:36:00.373 回答
1

我使用 BeginInvoke 的方式通常是使用我不关心它们何时完成或它们需要多长时间来处理的线程。

听起来您应该考虑使用Thread类而不是异步委托。

如果您关心结果检测错误(这两者都需要将结果/错误编组到原始线程),那么您可以使用异步委托,在这种情况下您需要EndInvoke. 更好的是,使用TaskorTask<TResult>类。

如果您只想分拆一些独立的操作,请使用Thread该类。

于 2011-01-03T18:18:40.090 回答
0

来自Control.BeginInvoke() 的 Windows 窗体文档

如果需要,您可以调用 EndInvoke 从委托中检索返回值,但这不是必需的。EndInvoke 将阻塞,直到可以检索到返回值。

这是UI 线程上 Windows 窗体异步调用的特殊情况,这不适用于一般情况,但这对处于这种情况的人有所帮助。

于 2016-03-14T05:43:23.930 回答