Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should this MVC4 API call be async?

I've just inherited an MVC4 API application that acts as a basic request forwarder for some other services. The basic structure of the app is something like this:

[request] -> [rest api] -> [local processing] -> [synchronous call to external service] -> [local processing of response] -> [response]

The local processing is mostly about validating and saving stuff into a db. Nothing heavy at all. The external request could take anything from 1 - 200+ seconds in extreme cases. The API typically handles several thousand requests per hour.

The app is hosted on a single, small, azure cloud instance.

What I'm confused about is the threading. The entire process from the API handler method, through to the external call to the external service, is set up to be asynchronous:

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();
}

So firstly, what is the advantage to making this process async? To my understanding, IIS should be managing its own thread pool for incoming requests, and since we're waiting for a synchronous response to a single external service call, it doesn't look like anything actually gets processed in parallel. At first glance, it looks like the async service requests would be competing for the same resources that IIS would use, and that it might actually be more efficient to just do it all synchronously.

I've read this: http://msdn.microsoft.com/en-us/library/ee728598%28v=vs.98%29.aspx?wa=wsignin1.0 and understand that there maybe something called 'thread starvation' on the IIS side. That article supports the idea that async might be helpful in this situation, but I'm struggling to understand why. Would it be easier to just increase the number of IIS threads? Would they not all be competing for the same resources? Is the issue that IIS threads are heavier to use?

like image 565
brettman Avatar asked Mar 20 '23 06:03

brettman


1 Answers

The use of the async/await does not mean "generate new threads to make these async calls".

Eric Lippert describes the process of using async/await beautifully in his serious Asynchronous Programming in C# 5:

The “async” modifier on the method does not mean “this method is automatically scheduled to run on a worker thread asynchronously”. It means the opposite of that; it means “this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” The whole point of async methods it that you stay on the current thread as much as possible. They’re like coroutines: async methods bring single-threaded cooperative multitasking to C#.

When you use the async modifier on a method, the compiler infers it as a sign and generates a state machine according to the flow of your method and the use of the await keyword inside it. It DOES NOT make any use of extra ThreadPool threads, on the contrary, when you await on I/O bound async method, the compiler yields control back to the caller, which in the case of ASP.NET will let the current thread return back to the ASP.NET ThreadPool to be used for other requests coming in. Once the I/O work is done, the continuation will be invoked via a IOCP thread also assigned by the ThreadPool, which means you are actually letting threads do more, without creating new ones at all.

There are plenty of great posts describing this effect:

  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?
like image 200
Yuval Itzchakov Avatar answered Apr 20 '23 12:04

Yuval Itzchakov