你是否必须为异步方法调用调用EndInvoke(或定义一个回调),即使你返回时没有任何事情要做

我在CodeProject上发现了以下代码片段,它在异步调用方法… http://www.codeproject.com/Articles/14931/Asynchronous-Method-Invocation

private void CallFooWithOutAndRefParameters() { // create the paramets to pass to the function string strParam1 = "Param1"; int intValue = 100; ArrayList list = new ArrayList(); list.Add("Item1"); // create the delegate DelegateWithOutAndRefParameters delFoo = new DelegateWithOutAndRefParameters(FooWithOutAndRefParameters); // call the beginInvoke function! IAsyncResult tag = delFoo.BeginInvoke(strParam1, out intValue, ref list, null, null); // normally control is returned right away, // so you can do other work here... // calling end invoke notice that intValue and list are passed // as arguments because they might be updated within the function. string strResult = delFoo.EndInvoke(out intValue, ref list, tag); // write down the parameters: Trace.WriteLine("param1: " + strParam1); Trace.WriteLine("param2: " + intValue); Trace.WriteLine("ArrayList count: " + list.Count); Trace.WriteLine("return value: " + strResult); } 

关于这段代码,我有几点我不明白。

根据注释控件在遇到BeginInvoke行时立即返回到调用代码。

这是否意味着后面的代码(EndInvoke后跟一些跟踪日志记录)仅在FooWithOutAndRefParameters调用完成后自动运行(即使该代码驻留在同一方法中)。 这对我来说有点混乱。 (我总是使用回调来做这种事情。)

使用此方法,我必须调用EndInvoke。 我可以只是异步调用该方法而忘记它发生了吗? 这有什么缺点吗?

如果我不调用EndInvoke(如此方法所示),那么我应该总是有一个回调吗? 即使回调什么都不做。

如果答案你应该……然后你调用EndInvoke还是定义一个回调? (定义回调的好处是您会收到结果通知)

BTW我知道我可以在EndInvoke或回调中检查错误或记录结果(事实上我可能会这样做)。 我想知道的是,有没有调用EndInvoke或定义回调(例如内存泄漏)的风险? 什么是最佳做法。

赛斯

是的,你必须调用EndInvoke()。 不这样做会导致相当令人讨厌的资源泄漏,持续10分钟。 底层管道是.NET Remoting,10分钟就是“默认终身租用时间”。 经常这样做,你的程序将完成。 让代表目标花费超过10分钟也会产生有趣的结果。

但最重要的是,如果不是调用操作的结果,则需要确定调用的方法是否成功完成。 如果它在exception中死亡,那么在调用EndInvoke()之前你不会发现它。 此时重新引发exception。 实际上处理该exception是相当棘手的,因为你不知道你的程序状态在爆炸之前被委托目标变异了多少。 如果你真的想抓住它,那么目标没有太多的副作用是非常重要的。 或者相反,目标捕获并重新抛出exception,根据需要执行状态恢复。

你使用的例子当然是非常愚蠢的,很难在一个方法中在BeginInvoke和EndInvoke调用之间做任何有用的事情。 您总是依赖于可以在BeginInvoke调用中注册的回调,倒数第二个参数。 或者换句话说,不要像示例代码那样传递null 。 并且喜欢BackgroundWorker和Task之类的类,甚至ThreadPool.QueueUserWorkItem()也可以从这段代码中解脱出来。 使用委托的BeginInvoke()方法是非常低级别的黑客攻击。

代表是.NET中非常强大的抽象,但他们没有很好地分发其function。 使用它们来实现事件是样板并且无故障。 正确使用它的BeginInvoke()方法是一种黑带艺术。 一个值得注意的细节是它不再适用于.NETCore,从CoreCLR中删除了对远程处理的支持。

在此之前,这些链接可能会引起关注:

异步调用同步方法

Delegate.EndInvoke()真的有必要吗?


现在,关于你的问题:

根据注释控件在遇到BeginInvoke行时立即返回到调用代码。

是的,调用是异步的(我可以假设它正在使用另一个线程)。

这是否意味着后面的代码(EndInvoke后跟一些跟踪日志记录)仅在FooWithOutAndRefParameters调用完成后自动运行(即使该代码驻留在同一方法中)。 这对我来说有点混乱。 (我总是使用回调来做这种事情。)

EndInvoke将阻止执行,直到BeginInvoke启动的线程(方法)完成。 在这种情况下,它类似于线程连接。

使用此方法,我必须调用EndInvoke。 我可以只是异步调用该方法而忘记它发生了吗? 这有什么缺点吗?

您必须始终调用EndInvoke (请参阅下文)。 这可能有很多原因,但我认为最重要的原因是,如果方法失败,通过抛出exception,在调用EndInvoke之前不会得到exception。

如果我不调用EndInvoke(如此方法所示),那么我应该总是有一个回调吗? 即使回调什么都不做。

在这种情况下,回调必须从回调中调用EndInvoke 。 所以只有回调是可选的。

如果答案你应该……然后你调用EndInvoke还是定义一个回调? (定义回调的好处是您会收到结果通知)

您不必定义回调,但如果这样做,则在其中调用EndInvoke。

您可以自行了解哪种情况更好:完全异步通知该方法已完成或强制使用该方法进行连接(从而阻止调用线程)。 这完全取决于控制,你应该做一个或另一个,甚至两个。

BTW我知道我可以在EndInvoke或回调中检查错误或记录结果(事实上我可能会这样做)。 我想知道的是,有没有调用EndInvoke或定义回调(例如内存泄漏)的风险? 什么是最佳做法。

不是天生就没有,我不相信存在风险。 但您必须始终检查方法是否失败或成功完成。


从MSDN ,以下是BeginInvoke之后可以执行的操作的选项:

  • 做一些工作,然后调用EndInvoke阻止,直到调用完成。

  • 使用IAsyncResult.AsyncWaitHandle属性获取WaitHandle,使用其WaitOne方法阻止执行,直到发出WaitHandle信号,然后调用EndInvoke。

  • 轮询BeginInvoke返回的IAsyncResult以确定异步调用何时完成,然后调用EndInvoke。

  • 将一个回调方法的委托传递给BeginInvoke。 异步调用完成时,该方法在ThreadPool线程上执行。 回调方法调用EndInvoke。


OBS:正如@ScottChamberlain在评论中所说, MSDN声明 :

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

我认为背后的原因是,在处理Controls时,你是在UI线程上操作。 由于EndInvoke会阻塞该线程,因此您可能有理由不想这样做。 不过,我建议使用回调或轮询来完成,以确保方法已成功完成。 这将使您的程序更健壮 (或错误恢复 )。

实际上,您应该调用EndInvoke,因为调用者可能让处理程序订阅了与您的处理无关的事件,但可能对消费者很重要,以确保在已知时间进行某些类型的处理/申请状态。