I have just started using the AsyncController in my project to take care of some long-running reports. Seemed ideal at the time since I could kick off the report and then perform a few other actions while waiting for it to come back and populate elements on the screen.
My controller looks a bit like this. I tried to use a thread to perform the long task which I’d hoped would free up the controller to take more requests:
public class ReportsController : AsyncController { public void LongRunningActionAsync() { AsyncManager.OutstandingOperations.Increment(); var newThread = new Thread(LongTask); newThread.Start(); } private void LongTask() { // Do something that takes a really long time //....... AsyncManager.OutstandingOperations.Decrement(); } public ActionResult LongRunningActionCompleted(string message) { // Set some data up on the view or something... return View(); } public JsonResult AnotherControllerAction() { // Do a quick task... return Json("..."); } }
But what I am finding is that when I call LongRunningAction using the jQuery ajax request, any further requests I make after that back up behind it and are not processed until LongRunningAction completes. For example, call LongRunningAction which takes 10 seconds and then call AnotherControllerAction which is less than a second. AnotherControllerAction simply waits until LongRunningAction completes before returning a result.
I’ve also checked the jQuery code, but this still happens if I specifically set “async: true”:
$.ajax({ async: true, type: "POST", url: "/Reports.aspx/LongRunningAction", dataType: "html", success: function(data, textStatus, XMLHttpRequest) { // ... }, error: function(XMLHttpRequest, textStatus, errorThrown) { // ... } });
At the moment I just have to assume that I’m using it incorrectly, but I’m hoping one of you guys can clear my mental block!
Advertisement
Answer
There are two issues in play here. The first is that your controller is not truly asynchronous. Spinning up a ThreadPool thread to perform work generally has worse performance characteristics than just doing everything from within the action method itself, as you’re still taking ThreadPool resources from ASP.NET (which just shares the CLR ThreadPool), and you’re now forcing the CLR and the OS to juggle threads. See http://msdn.microsoft.com/en-us/library/ee728598.aspx#choosing_synchronous_or_asynchronous_action_methods for more information. Basically, what that link boils down to is that if you can’t use I/O completion ports for your asynchronous operations, you’re very unlikely to see improved performance.
The second issue is that ASP.NET MVC takes a Session lock on all requests. Multiple requests within a single Session will always be serialized, as otherwise the user’s Session could become corrupted if one controller writes to Session as another controller is trying to read it. See http://forums.asp.net/t/1501623.aspx for context and a workaround. MVC 2 Futures has a way of disabling this lock; it may also be included in MVC 3 proper. See Link for more information on this.