【问题标题】:How to change async method call to prevent forcing async up the call stack如何更改异步方法调用以防止强制异步调用堆栈
【发布时间】:2014-03-30 03:09:29
【问题描述】:

如果我需要调用一个方法,而该方法又在内部调用某个异步方法,作为触发后忘记操作,我该如何防止此调用强制“异步”需要用完调用堆栈才能说。 ..一个 MVC 控制器?

例如:我的 MVC 控制器(非异步)调用业务层方法,该方法又调用 Windows Azure 服务总线 QueueClient.SendAsync(BrokeredMessage),将消息放入队列,但不需要等待让它完成。

通常情况下,当调用此控制器操作时,编译器会抛出此时无法启动异步操作的错误。

我知道不是等待或只是调用 SendAsync() 方法,我可以用 ContinueWith() 跟进它,以便在异步操作的回调上执行代码,但有人告诉我这不是正确的解决方案。 (参见Calling async method in controller的回复)

有人愿意告诉我解决这种情况的最佳方法吗?并告诉我为什么 ContinueWith() 方法不正确?

【问题讨论】:

    标签: c# asp.net-mvc asynchronous task-parallel-library servicebus


    【解决方案1】:

    调用 Windows Azure 服务总线 QueueClient.SendAsync(BrokeredMessage),将消息放入队列,但不需要等待它完成。

    首先,我会重新考虑这个假设。如果您想要一个完全可靠的系统,操作应该等待消息发送到总线。请注意,这通常是一个快速操作(100 毫秒)。

    通常情况下,当调用此控制器操作时,编译器会抛出此时无法启动异步操作的错误。

    确保您没有任何async void 方法或EAP method calls。如果 Azure 存储库导致该异常,我会感到非常惊讶。

    有人愿意告诉我解决这种情况的最佳方法吗?并告诉我为什么 ContinueWith() 方法不正确?

    最好的解决方案是接受asyncContinueWith 可以工作,但使用起来很危险(它有很多参数,其中一些具有不安全的默认值); await 实际上与 ContinueWith 相同,但没有危险的默认值。


    但是,如果 100 毫秒确实无法忍受,并且您愿意放弃可靠性(在这种情况下,这意味着您接受这样一个事实,即某些消息可能不会发送到总线,即使操作成功完成,因此客户端认为他们这样做了),那么您可以使用BackgroundTaskManager from my blog 将丢失消息的可能性降到最低。

    【讨论】:

    • 我理解您在丢弃消息时关于可靠性的第一点。此时我可能不会将我的 MVC 控制器更改为异步,或者将操作更改为异步方法,因为这假设我将业务层的接口更改为异步方法(一直采用异步)。如果我使用 ContinueWith() 但使用重载传递 CancellationToken.None、TaskContinuationOptions.DenyChildAttach、TaskScheduler.Default,那不应该注意默认 ContinueWith() 重载的“危险性”并给我可靠性方面吗?
    • 指定所有参数会解决“危险”问题,但不会为您提供可靠性。您的建议与致电Task.Run 基本相同。如果您不想将业务层(等)更改为异步,那么您应该调用Send 而不是SendAsync
    【解决方案2】:

    MVC 控制器方法处理 HTTP 请求并将相应的 HTTP 响应发送回客户端。如果我正确理解了您的问题,您想从异步 MVC 控制器方法中调用即发即弃方法,然后继续进行 HTTP 响应传递,因此即发即弃方法不会保存响应。

    确实,您不能以这种方式使用您的控制器方法触发它,而无需等待其结果。也就是说,您可以,但如果您的 ASP.NET 应用程序将重新启动,或者 IIS 服务器从场中取出,您的 ContinueWith 回调将永远不会被调用。因此,您不会知道该请求是否已到达 Windows Azure 服务。

    解决此问题的一种方法是在同一主机或同一网络上的另一台主机上运行帮助程序 Web API 或 WCF 服务(因此服务调用的周转速度非常快)。它可以是自托管服务。您可以从您的 MVC 控制器调用此帮助服务,只是为了 queue 即发即弃操作。你会await这个调用的结果,但在这种情况下这不是问题,因为这个操作的调用者和被调用者都存在于同一个网络上。

    这样,原始 MVC HTTP 响应将不会被搁置。在辅助服务中,您可以通过await QueueClient.SendAsync() 调用 Windows Azure 总线并相应地处理此操作的结果。

    【讨论】:

    • 我认为启动 Web API 并将控制器调用从内部业务层更改为同一业务层的 HTTP 端点外观是矫枉过正的......而且不是我愿意做的事情。另外,我的控制器不是异步的。它简单地调用业务层 DLL,进而利用具有异步方法的 Azure SB API。我不想将我的控制器更改为异步,或者在控制器中有异步操作。
    • @ThiagoSilva,我当然不清楚您的控制器不是异步的。
    • 抱歉,如果我不够清楚,但我感谢您的回复!
    猜你喜欢
    • 2019-08-03
    • 2016-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 2016-07-20
    相关资源
    最近更新 更多