4

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?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
brettman
  • 551
  • 5
  • 17
  • 1
    Finally I see a good example of async on the server. Most cases of async IO that I see on Stack Overflow do not benefit at all. Good that you are questioning this design decision! – usr Jun 23 '14 at 13:45

2 Answers2

4

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?
Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • "DOES NOT makes any use of extra ThreadPool threads"... except when it does. HttpClientHandler, which is used by System.Net.Http.HttpClient calls Task.Factory.StartNew when you send a request. This uses ThreadPool threads. Although this is an example of what should NOT be done, the OP's question refers to HttpClient. – bart Mar 30 '15 at 00:42
  • @bart - This wasn't specific to `HttpClient`. I know that internally it uses an extra thread, as you can see my question on that exactly [here](http://stackoverflow.com/questions/24983635/why-is-httpclient-sendasync-using-the-thread-pool-to-run-its-work-shouldnt-it). It was a general guideline. – Yuval Itzchakov Mar 30 '15 at 01:16
3

Since the external call can take up to 200 seconds, then yes, this appears to be an entirely appropriate use of an asynchronous controller.

Async controllers are best used when you have a large number of non-CPU bound blocking requests--I/O, as you have now, is a perfect example. A standard "synchronous" approach would block a thread for the duration of the external service call. Imagine you have 10 IIS worker threads (an artificially low number, I know), and receive 10 service calls more or less simultaneously. Each service call, for some reason (maybe due to the parameters passed), takes 100 seconds. As each thread is waiting for a response from the external service, your thread pool has been starved--IIS is not able to process any additional clients.

Using an async call, on the other hand, the .NET framework "shelves" the request to the external service, delegating it to a system process. The thread is then freed up to process additional clients. Once the request returns, execution flow on that particular thread is returned to the async method. If you're familiar with Javascript, you can imagine everything after the await as a callback method.

This is highly advantageous to simply increasing the number of threads as async methods do not require an expensive context switch when execution flow is returned after the await. All in all, async provides a better way of dealing with concurrent I/O-bound operations (note: async loses some of its advantage when dealing with CPU-heavy concurrent processes).

This article may help: http://blog.stevensanderson.com/2010/01/25/measuring-the-performance-of-asynchronous-controllers/

Lawrence Craft
  • 314
  • 1
  • 4