【问题标题】:Is it necessary to call EndInvoke in the callback from an EventHandler.BeginInvoke (C# .Net 3.5)是否有必要在 EventHandler.BeginInvoke (C# .Net 3.5) 的回调中调用 EndInvoke
【发布时间】:2010-11-15 17:38:27
【问题描述】:

我正在设置一个需要可靠和可扩展的 WCF 客户端和服务器方案,作为其中的一部分,我正在尝试使其成为多线程。诚然,我没有丰富的多线程经验。

在客户端,来自服务器的回调(在本例中为“OnMessage”)会触发应在后台传递的客户端事件。我将客户端的 CallbackBehavior ConcurrencyMode 设置为 Single(至少目前是这样)。因此,为了回复回调,而不是像往常一样调用处理程序(处理程序(发送者、事件参数或处理程序.Invoke...),我正在调用处理程序.BeginInvoke。

一切正常,但在回调中我想知道是否需要显式调用 EndInvoke 或者我是否基本上什么都不做(这是我缺乏多线程经验的表现)。

public void OnMessage(Message message)
{
    EventHandler<MessageEventArgs> handler = OnServerMessage;
    if (handler != null)
        handler.BeginInvoke(this, new MessageEventArgs(message), CompleteHandler, handler);
}

public void CompleteHandler(IAsyncResult result)
{
    ((EventHandler<MessageEventArgs>)result.AsyncState).EndInvoke(result);
}

我可以将 CompleteHandler 替换为对 handler.EndInvoke 的直接引用,或者一个空方法,或者我应该做些什么吗?

此外,目前,事件订阅者会阻塞等待来自控制台的用户输入。当应用程序完成时,它会阻塞等待数据库,或者是异步自下而上(所以这可能没有必要,但我仍然想知道)。

【问题讨论】:

    标签: c# .net multithreading events asynchronous


    【解决方案1】:

    是的,你必须这样做。找出调用的方法是否引发异常的唯一方法。并清理调用的远程处理状态,以便它可以被垃圾收集,而不是让它再逗留 10 分钟。

    【讨论】:

    • 感谢您的信息。这是我发现自己不知道多少的情况之一。我需要做什么来“清理远程状态”?如果您可以建议任何文章/文档/示例来阅读和学习,我也会很感激。再次感谢。
    • 要清理远程状态,你只需要调用EndInvoke :)
    • 您的回答暗示它会徘徊一段时间,然后无论如何都有资格进行垃圾收集。你确定吗?
    • @Simon - 一个新问题的好话题!
    • 对于微软来说,您没有调用 EndInvoke。引用:“如果需要,您可以调用 EndInvoke 从委托中检索返回值,但这不是必需的。EndInvoke 将阻塞,直到可以检索到返回值。”来自:msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.90).aspx
    【解决方案2】:

    您需要将CompleteHandler 代码包装在try/catch 块中,然后正确处理EndInvoke 引发的任何异常。目前该服务在“实验室”条件下工作,但当出现问题时,您甚至不会知道 什么 出了问题,因为您没有正确处理/记录错误。

    关于从全局状态,或从 IAsyncResult,或从回调闭包获取状态的问题:IASyncResult.AsyncState 是最通用的解决方案,在其他人重构调用代码后将继续正常工作识别。

    【讨论】:

    • 关于您的第二段:对不起,但我不完全理解。您是指我如何访问处理程序来调用 EndInvoke 吗?如果是这样,那么您的意思是建议将其作为状态对象传入并在 IAsyncResult.AsyncState 的回调中检索它以调用 EndInvoke 的方法是正确的还是不正确的?谢谢。
    • @Chris:我想我误解了这个问题。我虽然你在问是否有另一种方法可以在CompleteHandler 中获取对handler 的引用。
    【解决方案3】:

    一般来说,BeginInvoke 启动一个异步操作,而EndInvoke 等待它完成。所以这取决于你的意图:如果你想保证你的异步代码完成,你需要EndInvoke,否则不需要。

    (但是请注意,如果您的 BeginInvoke 调用到您实际所在的 same 线程(这是一个有效的用法,只是代码将在下一个 Dispatcher 循环中运行),您不得使用EndInvoke,因为这会造成死锁。但这似乎不是你的情况。)

    【讨论】:

    • 谢谢。但是,我不知道调用同一个线程首先涉及什么,所以我也不知道将来如何避免/控制它。你有一个例子说明它与我当前的实现有何不同?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多