When I first saw the Asynchronous Controllers introduced in MVC 2, I couldn't wait to start playing around. I thought that it would be a really easy to get up and running with a simple example, but after searching online for a few working examples - pretty much all of them used an event based pattern that wasn't easy to understand. In this blog post I am going to run through a really simple example that will show you the basics of Asynchronous Controllers, and show you how easy they really are.
There are some massive benefits to using Asynchronous Controllers with MVC. If your application is I/O or network limited, adding an AsynchronousController can help your application stay responsive. It's also a really sexy way of handling long running operations without blocking your application. In this post, I am going to run through a simple example using an Asynchronous Controller and show you just how easy it really is to get up and running. The easiest way by far is using the Task Parallel library. I have also blogged about the Task Parallel Library using Parallel Invoke, for more information please read this post.
Right, let's get started. In Visual Studio, create a new empty MVC application and add a new controller. I've called mine HomeController. Then you need to change the Controller so that it inherits from the AsyncController base class instead of the Controller base class.
Next up, I am going to add a method called IndexAysnc(). Asynchronous actions are written in a similar fashion to standard synchronous requests. It is important to name the Action Methods correctly, as they need to end in 'Async' or 'Completed'. In a synchronous controller, you would normally only have one Action Method per HTTP action. However, in an Async method, you get both Async and Completed appended to the
name - but the View name will still be Index. So in this example, our URL will still be /Home/Index
If you notice in the method above, it is making use of the Task.Factory.StartNew() method that comes with the Task Parallel Library. It is a great way to add threading to your application and it also makes the code really streamline. Inside the StartNew() method I am adding some simple code to download the HTML from this website, but you could replace this with any custom long running method of your choice. Next, add the final part of the async method (Completed). If we follow the naming convention, it should be IndexCompleted.
You may also notice that in the code above that I am using AsyncManager.OutstandingOperations. This notifies the MVC pipeline of how many operations are pending completion. This is necessary because MVC has no way of knowing what operations were kicked off by the action method or when those operations are complete. When the counter hits zero, the pipeline understands that it needs to complete the overall operation.
This is just a simple method that returns the data to our view, from here you can return anything that was executed asynchronously to your View. It's that simple! If you would like to run through this example the project is available for download, please click here to download.
Andrew Herrick - 1/17/2012
This is a great intro into Async, however I am trying to get my head wrapped around why I would use this? In your example, what would be the difference in just having the first request go out, updating data, then doing the second request? It would still take just as long to display the view?
Todd A. Wood - 1/17/2012
The Asynchronous name lures you in but your UI experience is still linked to the longest pole in the tent. There is no advantage for a single long running task as the AsyncController will take as long as a standard Controller. Few real world scenarios when you need to issue two independent LRTs.
Ariel de Llano - 1/17/2012
If the first task takes, let's say, 2 seconds and the second task takes 2 more, doing the two tasks synchronously will take 4 seconds total, doing it with an async controller will only take 2 seconds as the two tasks run in parallel.
mark erickson - 1/17/2012
Yes, thanks for a good article on a tough subject! @Andrew, I'm still new to async, but I think *this* async example cuts the *server* tasks into smaller pieces so the server workload is more efficient (= faster average responses for everyone).
Drogon - 1/17/2012
Very clear demo, thanks!
But I'm still perplexed about one thing:
Wouldn't this same thing be possible using a synchronous controller, making 2 parallel calls in the action method and then returning when all callbacks are completed?
mark erickson - 1/17/2012
I'm no expert but thats kind of how async works:
Server Thread#1 does DownloadString("data") until Begin()
Thread#1 is released
Thread#2 does DownloadString("moredata") until Begin()
Thread#2 is released
Eventually, both "data" and "moredata" come back--End()
Thread#3 Renders Response
ABC - 1/18/2012
Asynchronous Controllers is intended for IO bound operation. Your example is CPU bound and DownloadString call is synchronous. Use DownloadStringAsync or BeginRequest to get the real benefit of Asynchrony in ASP.NET
Martin - 1/18/2012
The main advantage to using AsyncController is that you are no longer blocking IIS threads so while that work is being done IIS can process more requests
Jarman - 1/18/2012
As Martin said, the primary benefit is to allow your server to process more requests. This article is pretty good:
Khoi Pham - 1/20/2012
I think some important information was left out in this post. Firstly you need to understand that execution of your task will not have access to the HttpContext unless you run it in the current SynchronizationContext.
Secondly, be aware that unobserved exceptions can kill the w3p.exe process!
Tun Hein - 1/23/2012
This is a nice stuff. But I was wondering, If we can use this in cascading dropdown circumstances.
Elliot - 12/4/2012
Thx for the article. Definitely makes sense for two http calls like in the example.
Now, I keep seeing people saying that asyc controllers allow IIS to have more of its threads back. But IIS threads aren't blocked after they pass the request to clr threads are they?
Elliot - 12/4/2012
Actually on second thoughts I am confused about the example. How is it better than a normal action method that waits for the two tasks to complete?