Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using "async" (even if it should complete) as part of a MVC route deadlocks the route; how can this be avoided?

Tags:

Consider the following (based on the default MVC template), which is a simplified version of some "stuff" that happens in the background - it completes fine, and shows the expected result, 20:

public ActionResult Index()
{
    var task = SlowDouble(10);
    string result;
    if (task.Wait(2000))
    {
        result = task.Result.ToString();
    }
    else
    {
        result = "timeout";
    }

    ViewBag.Message = result;
    return View();
}
internal static Task<long> SlowDouble(long val)
{
    TaskCompletionSource<long> result = new TaskCompletionSource<long>();
    ThreadPool.QueueUserWorkItem(delegate
    {
        Thread.Sleep(50);
        result.SetResult(val * 2);
    });
    return result.Task;
}

However, now if we add some async into the mix:

public static async Task<long> IndirectSlowDouble(long val)
{
    long result = await SlowDouble(val);

    return result;
}

and change the first line in the route to:

var task = IndirectSlowDouble(10);

then it does not work; it times out instead. If we add breakpoints, the return result; in the async method only happens after the route has already completed - basically, it looks like the system is unwilling to use any thread to resume the async operation until after the request has finished. Worse: if we had used .Wait() (or accessed .Result), then it will totally deadlock.

So: what is with that? The obvious workaround is "don't involve async", but that is not easy when consuming libraries etc. Ultimately, there is no functional difference between SlowDouble and IndirectSlowDouble (although there is obvious a structural difference).

Note: the exact same thing in a console / winform / etc will work fine.

like image 489
Marc Gravell Avatar asked Nov 29 '12 08:11

Marc Gravell


People also ask

What is async controller in MVC why one should use it?

The asynchronous controller enables you to write asynchronous action methods. It allows you to perform long running operation(s) without making the running thread idle. It does not mean it will take lesser time to complete the action.

What is the primary purpose of using an async controller action Mcq?

async actions help best when the actions does some I\O operations to DB or some network bound calls where the thread that processes the request will be stalled before it gets answer from the DB or network bound call which you just invoked.

What is constraints to a route in MVC?

The Route Constraint in ASP.NET MVC Routing allows us to apply a regular expression to a URL segment to restrict whether the route will match the request. In simple words, we can say that the Route constraint is a way to put some validation around the defined route.

What is default route in MVC?

The default route table contains a single route (named Default). The Default route maps the first segment of a URL to a controller name, the second segment of a URL to a controller action, and the third segment to a parameter named id.


1 Answers

It's to do with the way the synchronization context is implemented in ASP.NET (Pre .NET 4.5). There's tons of questions about this behavior:

Task.WaitAll hanging with multiple awaitable tasks in ASP.NET

Asp.net SynchronizationContext locks HttpApplication for async continuations?

In ASP.NET 4.5, there's a new implementation of the sync context that's described in this article.

http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-httpruntime-targetframework.aspx

like image 96
davidfowl Avatar answered Oct 14 '22 14:10

davidfowl