我应该使用异步Action方法吗?

时间:2014-03-09 02:12:29

标签: c# asp.net-mvc multithreading asynchronous async-await

我需要在我的Controller中实现一个异步Action,以便进行长时间运行的外部API调用。

看一些教程,我实现了我的方法:

    [AsyncTimeout(200)]
    public async Task<ActionResult> DoAsync()
    {
        // Execute long running call.
        return View();
    }

我的问题是,这是否足以使真正的非块异步?我是否还需要应用await运算符,如果是,我该怎么做?

4 个答案:

答案 0 :(得分:9)

要编写非阻塞异步代码,您需要执行某种现有的非阻塞异步操作,例如Task.Delay()或异步网络或文件IO。

简而言之,await关键字消费异步;它没有创造它。

如果您没有任何实际的异步工作,await对您没有任何好处。

答案 1 :(得分:6)

  

我需要在我的Controller中实现异步Action,以便进行长时间运行的外部API调用。
...
  我的问题是,这足以使真正的非块异步吗?我是否还需要应用await运算符,如果是,我该怎么做?

C#编译器可能已经建议async关键字在这里是多余的:

[AsyncTimeout(200)]
public async Task<ActionResult> DoAsync()
{
    // Execute long running call.
    return View();
}

您添加了async关键字这一事实并未使您的方法在后台神奇地运行。

如果按照another answer的建议执行await Task.Run(() => View())之类的操作,您仍然无法突破给定HTTP请求的界限。请求处理至少需要花费尽可能多的时间来生成View而不是Task.Run。客户端浏览器仍在等待它。

当您需要将CPU绑定的工作卸载到池线程时,此模式适用于UI应用程序,以避免阻止UI线程并保持UI响应。但是,在ASP.NET应用程序内的HTTP请求处理程序中使用它几乎不是一个好主意。它只会损害性能和可扩展性。

View花费大量时间撰写时提供用户友好体验的一个解决方案是运行跨越单个HTTP请求边界的后台任务 。然后进一步使用AJAX请求来保持客户端浏览器的进度更新。以下是Alan D. Jackson的一个很好的例子:

Long Running Background Tasks in Asp.Net MVC3

但是,在同一个ASP.NET服务器进程中跨多个HTTP请求运行冗长的后台操作是一个非常好的主意。虽然它相对容易实现,但这种方法可能会产生IIS可维护性,可伸缩性和安全性方面的问题。

对于单独的Windows/WCF service,您可能会更好,这会暴露基于Task的API。然后使用AJAX定期轮询WCF服务,使用ASP.NET MVC控制器的专用方法作为轮询调用的代理。

答案 2 :(得分:0)

正如另一个人写的那样,你需要一个异步动作来等待它,让View()异步包装在Task.Run

[AsyncTimeout(200)]
public async Task<ActionResult> DoAsync()
{
    // Execute long running call.
    return await Task.Run(() => View());
}

之后,此方法主要用于从其他异步函数或回调调用。

答案 3 :(得分:0)

  

我的问题是,这是否足以使真正的非块异步?我是否还需要应用await运算符,如果是,我该怎么做?

您应该在异步MVC操作中使用await关键字,原因如下:

  1. 如果您的操作更改了应用程序的状态(更新数据库等),则await关键字使您可以在外部服务失败的情况下回滚更改。否则这可能会导致状态不一致,因为用户不能只重播给定的异步操作,它只能重放整个动作。
  2. 托管环境(IIS)可能导致应用域因different reasons而卸载。当它发生时,你的应用程序可以永远获得异步操作的结果。在常规请求处理案例中,ASP.NET等待每个操作完成。如果某些请求需要很长时间才能完成(并且超过关闭超时),ASP.NET将中止它们并发送失败响应。
  3. 这就是为什么你不应该使用async而不使用await,也不应该使用其他.NET异步技术(如TPL)。在这种情况下,使用WCF的自定义Windows服务是一种更好的解决方案,但它使编程,部署和维护任务变得非常复杂。

    当然,您可以使用HostingEnvironment.RegisterObject,此处为a good article。但是当ASP.NET调用IRegisteredObject接口实现的Stop方法时(在关闭期间),您只有30秒(默认情况下)保存数据。如果您具有未完成的异步操作,则应中止它们,将它们标记为失败并在重新启动后重试它们或向用户发送失败通知。如果您的存储空间目前也不可用,请告知您的结果(包括失败的结果)。

    您还可以使用持久队列,可靠的提取以及在ASP.NET应用程序中侦听这些队列的专用工作程序。即使工作进程终止,这也可以保护您。此外,还有很多项目,例如ResqueSidekiqPyres等,但它们适用于其他语言。

    对于.NET,尝试HangFire - 它正在开发中,但是比这些系统的初始实现更稳定,并且具有许多不同的功能。