这个MVC4 API调用应该是异步的吗?

时间:2014-06-23 12:47:37

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

我刚刚继承了一个MVC4 API应用程序,它充当其他一些服务的基本请求转发器。该应用程序的基本结构是这样的:

[request] - > [rest api] - > [本地处理] - > [同步​​呼叫外部服务] - > [响应的本地处理] - > [反应]

本地处理主要是验证并将内容保存到数据库中。什么都没有重。在极端情况下,外部请求可能需要1到200秒以上的时间。 API通常每小时处理数千个请求。

该应用程序托管在一个小型的天蓝云实例上。

我感到困惑的是线程。从API处理程序方法到外部服务的外部调用的整个过程都设置为异步:

public async Task<CustomResponseType> Post([FromBody] inputType) {
   // some validation
   ...
   // save some stuff to a db
   ...

   var response = await httpClient.PostAsync(someRequest)
   return await response.Content.ReadAsStringAsync();
}

首先,使这个过程异步的优势是什么?根据我的理解,IIS应该为传入的请求管理自己的线程池,并且由于我们正在等待对单个外部服务调用的同步响应,因此它看起来并不像实际上并行处理的任何事情。乍一看,看起来异步服务请求将竞争IIS将使用的相同资源,并且实际上同步执行它可能更有效。

我读过这个:http://msdn.microsoft.com/en-us/library/ee728598%28v=vs.98%29.aspx?wa=wsignin1.0并且明白在IIS端可能存在一个叫做“线程饥饿”的东西。那篇文章支持在这种情况下异步可能有用的想法,但我很难理解为什么。是否更容易增加IIS线程的数量?他们不是都会竞争同样的资源吗? IIS线程使用的问题是否更重?

2 个答案:

答案 0 :(得分:4)

使用async/await并不意味着“生成新线程来进行这些异步调用”。

Eric Lippert描述了在他严肃的Asynchronous Programming in C# 5中精美地使用async/await的过程:

  

方法中的“async”修饰符并不意味着“此方法是   自动安排在工作线程上异步运行“。它   意思是与此相反;它意味着“此方法包含控件   涉及等待异步操作的流程,因此   由编译器重写为延续传递样式以确保   异步操作可以在右侧恢复此方法   现场。“异步方法的重点在于它   当前线程尽可能多。他们就像协程:异步   方法将单线程协同多任务处理带到C#。

当您在方法上使用async修饰符时,编译器会将其推断为符号,并根据您的方法流和在其中使用await关键字生成状态机。它不会使用额外的ThreadPool线程,相反,当你在I / O绑定异步方法上await时,编译器会将控制权交还给调用者,在这种情况下ASP.NET将让当前线程返回到ASP.NET ThreadPool以用于其他请求。一旦I / O工作完成,将通过ThreadPool分配的IOCP线程调用continuation,这意味着你实际上让线程做得更多,而根本没有创建新线程。

有很多很棒的帖子描述了这种效果:

  1. There Is No Thread - By Stephan Cleary
  2. Does async and await increase performance of an ASP.Net application
  3. ASP.NET async/await part 2
  4. The Magic of using Asynchronous Methods in ASP.NET 4.5 plus an important gotcha (By Scott Hanselman)
  5. Why use async requests instead of using a larger threadpool?

答案 1 :(得分:3)

由于外部呼叫最多需要200秒,然后是,这似乎完全适合使用异步控制器。

当您拥有大量非CPU绑定阻塞请求时,最好使用异步控制器 - 正如您现在所做的那样,I / O就是一个完美的示例。标准的“同步”方法会在外部服务调用期间阻塞线程。想象一下,你有10个IIS工作线程(人为的数字,我知道),并且或多或少同时接收10个服务调用。由于某种原因(可能由于传递的参数),每次服务调用需要100秒。当每个线程都在等待来自外部服务的响应时,您的线程池已经饿死了 - IIS无法处理任何其他客户端。

另一方面,使用异步调用,.NET框架将请求“搁置”到外部服务,并将其委派给系统进程。然后释放该线程以处理其他客户端。请求返回后,该特定线程上的执行流将返回到异步方法。如果您熟悉Javascript,则可以将await之后的所有内容想象为回调方法。

这非常有利于简单地增加线程数,因为async方法在await之后返回执行流时不需要昂贵的上下文切换。总而言之,async提供了一种处理并发I / O绑定操作的更好方法(注意:在处理CPU繁重的并发进程时,async失去了一些优势)。

本文可能有所帮助:http://blog.stevensanderson.com/2010/01/25/measuring-the-performance-of-asynchronous-controllers/