【问题标题】:Do you have to call EndInvoke (or define a callback ) for asynchronous method calls even if you don't have anything to do when it returns是否必须为异步方法调用调用 EndInvoke (或定义回调),即使它返回时您无事可做
【发布时间】:2012-07-23 20:50:38
【问题描述】:

我在 CodeProject 上找到了以下代码 sn-p 关于异步调用方法的... 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 还是定义一个回调? (定义回调的好处是通知您结果)

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

赛斯

【问题讨论】:

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


【解决方案1】:

是的,您必须调用 EndInvoke()。不这样做会导致持续 10 分钟的相当严重的资源泄漏。底层管道是 .NET Remoting,10 分钟是“默认生命周期租用时间”。经常这样做,你的程序就会崩溃。让委托目标花费超过 10 分钟也有有趣的结果。

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

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

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

【讨论】:

  • 目标没有太多副作用是非常重要的——非常好的建议。
【解决方案2】:

首先,这些链接可能会引起人们的兴趣:

Calling Synchronous Methods Asynchronously

Is Delegate.EndInvoke() really necessary?


现在,关于您的问题:

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

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

这是否意味着后面的代码(EndInvoke 后跟一些 跟踪日志记录)仅在 FooWithOutAndRefParameters 调用之后运行 完成...自动(即使该代码位于同一 方法)。我看起来有点困惑。 (我一直用 这类事情的回调。)

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

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

您必须始终致电EndInvoke(见下文)。造成这种情况的原因可能有很多,但我认为最重要的原因是,如果方法失败,通过抛出异常,在调用 EndInvoke 之前您不会收到异常。

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

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

如果答案是您应该...那么您是调用 EndInvoke 还是定义一个 打回来? (定义回调的好处是你是 结果通知)

你不必定义回调,但如果你定义了,你会在其中调用 EndInvoke。

由您决定哪种方案更好:完全异步地通知方法完成或强制与方法连接(从而阻塞调用线程)。一切都与控制有关,您应该做一个或另一个,甚至两者都做。

顺便说一句,我知道我可以在 EndInvoke 或 回调(实际上我可能会这样做)。我想知道的是 不调用 EndInvoke 或定义回调(内存 例如泄漏)?最佳做法是什么。

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


来自MSDN,以下是 BeginInvoke 之后可以做什么的选项:

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

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

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

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


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

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

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

【讨论】:

  • 要添加到 MSDN 中的“是 Delegate.EndInvoke 真的必要”链接,他们明确声明 EndInvoke is optional for controls
  • @ScottChamberlain,对此一无所知。我会更新答案。
  • Control.EndInvoke()Delegate.EndInvoke() 完全不同。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 2019-10-09
    • 2019-05-22
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-20
    • 2018-08-12
    • 1970-01-01
    相关资源
    最近更新 更多