My Take on Task-base Asynchronous Programming in C# 5.0 and ASP.NET MVC Web Applications

I'm trying to show you what new C# 5.0 can bring us in terms of asynchronous programming with await keyword. Especially on ASP.NET MVC 4 Web Applications.
2012-02-26 19:21
Tugberk Ugurlu


I have been playing with Visual Studio Async CTP for a while now and I had enough idea on how it works and how I can use it for my benefit and that means that I am able to blog about it.

On most of the softwares we build, the main problem occurs on long running operations. If a user starts a long running operation, that operation blocks the main thread till it completes. Doing so will result pretty unusable applications and unhappy customers for your business. I am sure that we all have used that kind of applications in our lives.

Asynchronous programming model is not a new paradigm and it has been available on .NET since v1.0. I wasn’t very interested in programming 3 years ago so this concept is fairly new to me even now. The concept has evolved a lot on .NET in terms of asynchronous programming as far as I read and I think it has reached its best stage.

On the other hand, you can get confused about asynchronous programming pretty much easily as I did several times. I went back and forth about how it works and where to use it. As a result, I believe I finally got it right. On the other hand, It is much easier to get confused if you’d like to leverage asynchrony on web applications because you do not have a UI thread to flush your results out immediately. Asynchronous or not, the user has to wait the amount of time operation takes to complete. This makes asynchronous programming undesirable for web applications but if you think that way, as I did, you are missing the point.

If you start an operation synchronously and it takes long time, you have no option rather than blocking that thread. If you do the same operation asynchronously, what happens is that you start an operation and come back later when it finishes without waiting it to be finished. Between that duration, your thread is free and can do other stuff as well. Most of the confusion happens here I think. Creating additional threads is not cheap and may cause issues but it is all in past I think. .NET 4.0 has increases the number of thread limits very dramatically. It is still not good to walk around and create threads but it is good not to worry about them. There are couple of debates going around about asynchronous programming (or should I have said went around?):

I am not sure what is the real answer of those questions. But here are two examples:

  • Windows Runtime (WinRT) is designed to be async friendly. Anything longer than 40ms (operations related to network, file system. Mainly I/O bound operations), that is async.
  • ASP.NET Web API introduced a new way of exposing your data to the World with different formats. One thing to notice is that there is nearly no synchronous method on their API. Not metaphorically, literally there is no synchronous versions of the some methods.

That’s being said, I think we have a point here that we should take advantage of asynchrony one way or another in our applications. So, what is the problem? The problem is the way how async programming model works. As Anders Hejlsberg always says, that programing model turns our code inside out. Not to mention that it is extremely hard to do nested async operations and exception handling.

Visual Studio Async CTP and How It Works

In C# 5.0, we will have a new asynchronous programming model which looks a lot like synchronous. On the other hand, C# team has released a CTP version of those features and it has a go-live license. Here is quote from the AsyncCTP spec:

Asynchronous functions is a new feature in C# which provides an easy means for expressing asynchronous operations. Inside asynchronous functions await expressions can await ongoing tasks, which causes the rest of the execution of the asynchronous function to be transparently signed up as a continuation of the awaited task. In other words, it becomes the job of the programming language, not the programmer, to express and sign up continuations. As a result, asynchronous code can retain its logical structure.

An asynchronous function is a method or anonymous function which is marked with the async modifier. An asynchronous function can either return Task or Task<T> for some T and both of them can be awaited. Those kind of functions can also return void but it cannot be awaited. On the other hand, you can await any type if that type satisfies a certain pattern.

ASP.NET MVC 4 and C# Async Features

Benefiting from asynchrony in a right way on ASP.NET MVC applications can result huge positive performance impact. Believe it or not it’s true. I’ll show you how.

So why don't we use it much? Because it is hard and error prone. In the long run, it is hard to maintain the application as well. But with new asynchronous programming model, it is about to change.

In ASP.NET MVC 4, asynchronous programming model has been changed a lot as well. As you probably know, in ASP.NET MVC 3, our controller has to be derived from AsyncController and must satisfy a certain pattern to work with. You can see the Using an Asynchronous Controller in ASP.NET MVC article if you would like to see how it works.

In ASP.NET MVC 4, we do not need AsyncController to leverage asynchrony in our applications. Our controller actions can be marked with async keyword and return Task or Task<T> where the T is usually the type of ActionResult.

I put together a sample application which does the same thing both asynchronously and synchronously. I also did a load test and the end result was shocking. Let’s see what the code looks like:

Firstly, I created a simple REST service endpoint with new REST hotness of .NET: ASP.NET Web API. I have a simple model and collection which I store it in memory. This would be normally a database instead of memory.

public class Car {

    public string Make;
    public string Model;
    public int Year;
    public int Doors;
    public string Colour;
    public float Price;
    public int Mileage;
}

public class CarService {

    public List<Car> GetCars() {

        List<Car> Cars = new List<Car> {

            new Car{Make="Audi",Model="A4",Year=1995,Doors=4,Colour="Red",Price=2995f,Mileage=122458},
            new Car{Make="Ford",Model="Focus",Year=2002,Doors=5,Colour="Black",Price=3250f,Mileage=68500},
            new Car{Make="BMW",Model="5 Series",Year=2006,Doors=4,Colour="Grey",Price=24950f,Mileage=19500}
            //This keeps going like that
        };

        return Cars;
    }
}

And here is my Web API:

public class CarsController : ApiController { 

    public IEnumerable<Car> Get() {

        var service = new CarService();

        return service.GetCars();
    }
}

I have my service now. In my web application, I will get the data from this service and display on the web page. To do that, I created a service class which gets the data from that endpoint and deserialize the string into an object. I used the new HttpClient for asynchronous version of GetCars operation and WebClient for synchronous version of it. I also used Json.NET for working with JSON payload.

public class CarRESTService {

    readonly string uri = "http://localhost:2236/api/cars";

    public List<Car> GetCars() { 

        using (WebClient webClient = new WebClient()) {
            
            return JsonConvert.DeserializeObject<List<Car>>(
                webClient.DownloadString(uri)
            );
        }
    }

    public async Task<List<Car>> GetCarsAsync() {

        using (HttpClient httpClient = new HttpClient()) {
            
            return JsonConvert.DeserializeObject<List<Car>>(
                await httpClient.GetStringAsync(uri)    
            );
        }
    }
}

Above GetCars method is very boring as you see. Nothing to talk about. The real deal is in the second method which is GetCarsAsync:

  • The method is marked with async keyword which indicates that the method has some asynchronous code.
  • We used await keyword before HttpClient.GetStringAsync method which returns Task<string>. But notice here that we use it as string. The await keyword enables that.

Lastly, here is my controller:

public class HomeController : Controller {

    private CarRESTService service = new CarRESTService();

    public async Task<ActionResult> Index() {

        return View("index",
            await service.GetCarsAsync()
        );
    }

    public ActionResult IndexSync() {

        return View("index",
            service.GetCars()
        );
    }
}

We have two actions here, one is Index which is an asynchronous function and returns Task<ActionResult> and the second one is IndexSync which is a typical ASP.NET MVC controller action. When we navigate to /home/index and /home/indexsync, we cannot really see the difference. It takes approx. the same time.

In order to measure the difference, I configured a load test with Visual Studio 2010 Ultimate Load Testing features. I hit the two pages for 2 minutes. I started with 50 users and it incremented by 20 users per 5 seconds and max user limit was 500. The result was really shocking in terms of page response time.

asyncFTW

While average response time for the synchronous one is about 11.2 seconds, it is 3.65 for asynchronous one. I think the difference is pretty compelling and overwhelming.

From now on, I am adopting the Windows approach: "Anything longer than 40ms (operations related to network, file system. Mainly I/O bound operations), that is async!"

If you believe that some of the information here is wrong or misleading, please make me suffer and post a comment which would bring me down. Also, do the same if you have any additional information that is worth mentioning on this post but I missed.

Resources



Comments

Chris Marisic
by Chris Marisic on Tuesday, Feb 28 2012 16:52:15 +02:00

Great post Tugberk. The load test results are just astounding.

Tugberk
by Tugberk on Tuesday, Feb 28 2012 17:26:29 +02:00

@Chris

Thanks! Yeah, the difference is obvious. After the post, I watched the Steve's talk on TechDays and he used the Appache Bench to measure the stress. His benchmarking gave the same difference.

I'm following your & MSFT's lead. If longer than 40ms, async FTW!

Ebrahim Byagowi
by Ebrahim Byagowi on Monday, Apr 09 2012 20:40:56 +03:00

I think using of WebClient for sync. test and HttpClient for async test made a bit difference here (not a lot I think). Can you run these tests both with HttpClient (if is applicable here of course, I have not access to visual studio for test that for myself right now :D). Thanks :)

Tugberk
by Tugberk on Monday, Apr 09 2012 21:40:55 +03:00

@Ebrahim

HttpClient has no synchronous methods. You may use WebClient.DownloadStringTaskAsync for async version of WebClient.DownloadString but I don't think there is going to be any difference since it is all about how you open up I/O completion ports.

Ebrahim Byagowi
by Ebrahim Byagowi on Friday, Apr 13 2012 03:22:07 +03:00

@Tugberk:

Thanks :)

Radim Köhler
by Radim Köhler on Friday, Nov 09 2012 10:26:17 +02:00

Briliant. When I learned MVC 3 into deep... ne MVC 4 is here. I was sure that async await could be great, but only for smart and fat clients. And what you have shown: new horizons. Incredible. Have to thank you. (stil so much to learn;)

Sunny
by Sunny on Sunday, May 05 2013 11:38:13 +03:00

Thanks for such a nice post... very informative... I really appreciate you shared it !!

Shina
by Shina on Sunday, Dec 27 2015 11:13:09 +00:00
Hi, Thanks for the post. I'm trying to do a dll for logging. this is accomplished by elasticsearch and NEST. The other company websites/services/apps will use this dll. When the site send to log() method, it actually needs no respond. So I don't find where I need to put the await syntax. can you please guide me how I should write this method? Thanks Shina

Tags