您是否必须为异步方法调用调用EndInvoke(或定义回调),即使它返回时没有任何操作

时间:2012-07-23 20:50:38

标签: c# .net vb.net asynchronous callback

我在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或定义回调(例如内存泄漏)的风险?什么是最佳做法。

赛斯

3 个答案:

答案 0 :(得分:12)

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

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

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

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

答案 1 :(得分:5)

在此之前,可能会对这些链接感兴趣:

Calling Synchronous Methods Asynchronously

Is Delegate.EndInvoke() really necessary?


现在,关于您的问题:

  

根据注释控件立即返回到调用代码   当它到达BeginInvoke行时。

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

  

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

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

  

使用此方法我是否必须调用EndInvoke。我可以调用吗?   方法异常并忘记它发生了?这有什么缺点吗?

您必须始终致电EndInvoke(见下文)。这可能有很多原因,但我认为最重要的原因是,如果方法失败,通过抛出异常,在调用EndInvoke之前,您将无法获得异常。

  

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

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

  

如果答案你应该...然后你调用EndInvoke或定义一个   打回来? (定义回调的好处在于你   通知结果)

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

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

  

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

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


MSDN开始,以下是BeginInvoke之后可以做的选项:

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

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

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

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


OBS:正如@ScottChamberlain在评论中所说,MSDN states that

  

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

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

答案 2 :(得分:1)

作为一种惯例,你应该调用EndInvoke,因为调用者可能让处理程序订阅了与你的处理无关的事件,但可能对使用者,以确保某些类型的处理可以在已知的时间/应用程序状态下进行。