Question

As I read the MSDN article Using Asynchronous Methods in ASP.NET MVC 4, I draw the conclusion that I should always use async await for I/O-bound operations.

Consider the following code, where movieManager exposes the async methods of an ORM like Entity Framework.

public class MovieController : Controller
{
    // fields and constructors

    public async Task<ActionResult> Index()
    {
        var movies = await movieManager.listAsync();

        return View(movies);
    }

    public async Task<ActionResult> Details(int id)
    {
        var movie = await movieManager.FindAsync(id);

        return View(movie);
    }
}
  1. Will this always give me better scalability and/or performance?
    • How can I measure this?
  2. Why isn't this used in the "real world"?
  3. How about context synchronization?
    • Is it that bad, that I shouldn't use async I/O in ASP.NET MVC?

I know these are a lot of questions, but literature on this topic has conflicting conclusions. Some say you should always use async for I/O dependent Tasks, others say you shouldn't use async in ASP.NET applications at all.

Was it helpful?

Solution

Will this always give me better scalability and/or performance?

It may. If you only have a single database server as your backend, then your database could be your scalability bottleneck, and in that case scaling your web server won't have any effect in the wider scope of your service as a whole.

How can I measure this?

With load testing. If you want a simple proof-of-concept, you can check out this gist of mine.

Why isn't this used in the "real world" a lot?

It is. Asynchronous request handlers before .NET 4.5 were quite painful to write, and a lot of companies just threw more hardware at the problem instead. Now that .NET 4.5 and async/await are gaining a lot of momentum, asynchronous request handling will continue to be much more common.

How about context synchronization?

It's handled for you by ASP.NET. I have an async intro on my blog that explains how await will capture the current SynchronizationContext when you await a task. In this case it's an AspNetSynchronizationContext that represents the request, so things like HttpContext.Current, culture, etc. all get preserved across await points automatically.

Is it that bad, that I shouldn't use async I/O in ASP.NET MVC?

As a general rule, if you're on .NET 4.5, you should use async to handle any request that requires I/O. If the request is simple (i.e., does not hit a database or call another service), then just keep it synchronous.

OTHER TIPS

Will this always give me better scalability and/or performance?

You answered it yourself, you need to measure and find out. Typically async is something to add later on due to adding complexity, which is the #1 concern in your code base until you have a problem that is specific.

How can I measure this?

Build it both ways, see which is faster (preferably for a large number of operations)

Why isn't this used in the "real world" a lot?

Because complexity is the biggest problem in software development. If code is complex it is more error prone and harder to debug. More, harder to fix bugs is not a good trade off for potential performance advantages.

How about context synchronization?

I am assuming you mean ASP.NET context, if so you should not have any synchronization, make sure only one thread is hitting your context and communicate through it.

Is it that bad, that I shouldn't use async I/O in ASP.NET MVC?

Introducing async just to then have to deal with synchronization is a loss unless you really need the performance.

Putting asynchronous code in a website has a lot of negative sides :

  • You'll get into trouble when there are dependencies between the pieces of data, as you cannot make that asynchronous.
  • Asynchronous work is often done for things like API requests. Have you considered that you shouldn't be doing these in a webpage? If the external service goes down, so goes your site. That doesn't scale.
  • Doing things asynchronously may speed up your site in some cases but you're basically introducing trouble. You always end up waiting for the slowest one, and since sometimes resources just slow down for whatever reason this means that the risk of something slowing down your site increases by a factor equal to the number of asynchronous jobs you're using. You'll have to introduce timeouts to deal with these, then error handling code, etc.
  • When scaling to multiple webservers because the CPU load is getting too heavy, the asynchronous work will hurt you. Everything you used to put in asynchronous code now fires simultaneously the moment the user clicks a link, and then eases down. This doesn't only apply to CPU load, but also database load and even API requests. You will see a very awful utilization pattern across all system resources: spikes of heavy usage, and then it goes down again. That doesn't scale well. Synchronous code doesn't have this problem: jobs only start after another one is done.

Asynchronous work for websites is a trap: don't go there!

Put your heavy code in a worker (or cron job) that does these things before the user asks for them. You'll have them in a database and you can keep adding features to your site without having to worry about firing too many asynchronous jobs and what not.

Performance for websites is seriously overrated. Sure, it's nice if your page renders in 50ms, but if it takes 250ms people really won't notice (to test this: put a Sleep(200) in your code).

Your code becomes a lot more scalable if you just offload the work to another process and make the website an interface to only your database. Don't make your webserver do heavy work that it shouldn't do, it doesn't scale. You can have a hundred machines spending a total of 1 CPU hour per webpage - but at least it scales in a way where the page still loads in 200ms. Good luck achieving that with asynchronous code.


I would like to add a side-note here. While my opinion on asynchronous code might seem strong, it's mostly an opinion about programmers. Asynchronous code is awesome and can make a performance difference that proves all of the points I outlined wrong. However, it needs a lot of finetuning in your code to avoid the points I mention in this post, and most programmers just can't handle that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top