使用MVC5中的异步有什么优势?

120

什么是以下两者的区别:

public ActionResult Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        IdentityResult result = IdentityManager.Authentication.CheckPasswordAndSignIn(AuthenticationManager, model.UserName, model.Password, model.RememberMe);
        if (result.Success)
        {
            return Redirect("~/home");
        }
        else
        {
            AddErrors(result);
        }
    }
    return View(model);
}

和:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        IdentityResult result = await IdentityManager.Authentication.CheckPasswordAndSignInAsync(AuthenticationManager, model.UserName, model.Password, model.RememberMe);
        if (result.Success)
        {
            return Redirect("~/home");
        }
        else
        {
            AddErrors(result);
        }
    }
    return View(model);
}

我看到MVC代码现在有异步,但是有什么区别呢?其中一个比另一个性能更好吗?是否更容易使用其中一个调试问题?我应该对我的应用程序的其他控制器进行更改以添加异步吗?


在绝大多数情况下,在MVC中使用异步操作并没有什么明显的好处,但是有很多负面影响。 - Chris Marisic
1
@ChrisMarisic - 其中最严重的问题之一是:您不能使用ReaderWriterLock或任何其他同步原语(除了Semaphore)。 - Quark Soup
3个回答

170
异步操作仅在执行I/O绑定操作(例如远程服务器调用)时才有用。异步调用的好处是在I/O操作期间不使用任何ASP.NET工作线程。以下是第一个示例的工作原理:
  1. 当请求到达操作时,ASP.NET从线程池中获取一个线程并开始执行它。
  2. 调用IdentityManager.Authentication.CheckPasswordAndSignIn方法,这是一个阻塞调用->在整个调用过程中,工作线程会受到威胁。
以下是第二次调用的工作原理:
  1. 当请求到达操作时,ASP.NET从线程池中获取一个线程并开始执行它。
  2. 调用IdentityManager.Authentication.CheckPasswordAndSignInAsync,该操作立即返回。注册了一个I/O完成端口,并释放ASP.NET工作线程到线程池。
  3. 稍后操作完成时,将信号发送到I/O完成端口,另一个线程从线程池中抽取以完成返回视图的工作。
如您所见,在第二种情况下,ASP.NET工作线程仅被使用短时间。这意味着池中有更多的线程可用于服务其他请求。
因此,总之,只有在内部具有真正的异步API时才使用异步操作。如果在异步操作中进行阻塞调用,则会破坏其全部好处。

关于上下文同步,这个会不会是一个开销很大的问题,让你不希望完全使用异步操作呢?“一个异步方法实际执行时的开销完全取决于它是否需要使用SynchronizationContext.Post来进行线程切换。如果需要,那么开销就由它恢复所执行的线程切换所支配。这意味着当前的SynchronizationContext对于开销有着非常重要的影响。”(C# 5.0中的Async, 2012年,Alex Davies) - annemartijn
1
@Darin 为什么释放主线程如此重要?线程有限制吗? - Omtechguy
1
@Omtechguy,更好的解决方案是将非ASP.NET请求移动到CDN上。简单的CDN仅使用子域和单独的应用程序池来处理物理文件,例如JavaScript和图像。或者,您可以使用NgineX / Lighttpd / Apache来处理文件,也可以使用第三方服务(例如Akamai,是CDN之王但价格最贵)。 - Chris Marisic
我仍然感到困惑。当调用CheckPasswordAndSignInAsync方法时,ASP.NET会从线程池中获取另一个线程并开始执行它,是吗?如果不是,那么检查密码的过程会在哪里执行? - KevinBui

2

通常情况下,一个单一的HTTP请求将由一个单独的线程处理,完全将该线程从池中移除,直到返回响应。使用TPL,您不受此约束的限制。任何进来的请求都会启动一个继承,每个计算单元需要计算一个响应都能在池中的任何线程上执行。有了这个模型,您可以处理比标准ASP.Net更多的并发请求。

如果是新的任务,是否应该生成它以及是否应该等待它。始终考虑那些70毫秒,这是任何方法调用所需的最长时间。如果超过时限,那么您的用户界面很可能无法感到非常灵敏。


0
在 Web 应用程序中,如果启动时或负载突然增加(并发性突然增加)的情况下看到大量并发请求,将这些 Web 服务调用异步化将增加应用程序的响应能力。异步请求处理所需的时间与同步请求相同。例如,如果请求进行需要两秒钟才能完成的 Web 服务调用,则无论是同步还是异步执行该请求都需要两秒钟。但是,在异步调用期间,线程不会被阻塞以等待第一个请求完成而无法响应其他请求。因此,异步请求可以防止请求排队和线程池增长,特别是在有许多并发请求调用长时间运行操作的情况下。

如果你的aspnet应用程序主要由对其他Web服务器的调用组成,那么你基本上是作为一个“网关”/“代理”行为,异步对于这个目的非常有用。 - Chris Marisic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接