2

I am trying to understand the async/await mechanism with MVC. Now I do not consider a use case where I would go out of the "normal flow" (using either full sync or full async end-to-end). I just want to make sure to understand the why it doesn't work here.

When "SyncMethod" is called it hangs indefinitely and never returns.

When "AsyncMethod" is called it returns a view quickly without hanging.

When "TaskMethod" is called it returns a view quickly without hanging as well.

I am not exactly sure to understand why when a synchronous method calls an async method it is impossible to return a result.

What am I missing here?

using System;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace MvcAsyncAwaitTest.Controllers
{
    public class HomeController : Controller
    {
        /// <summary>
        /// Synchronous method running async method as sync
        /// Hangs at Hello().Result, never returns
        /// </summary>
        /// <returns></returns>
        public ActionResult SyncMethod()
        {
            ViewBag.Message = Hello().Result;
            return View();
        }

        /// <summary>
        /// Asynchronous method awaiting asynchronous method
        /// Do not hang 
        /// </summary>
        /// <returns></returns>
        public async Task<ActionResult> AsyncMethod()
        {
            ViewBag.Message = await Hello();
            return View("Index");
        }

        /// <summary>
        /// Synchronous method running a task based method synchronously
        /// Returns a valid result
        /// </summary>
        /// <returns></returns>
        public ActionResult TaskMethod()
        {
            ViewBag.Message = Hello2().Result;

            return View("index");
        }

        private async Task<string> Hello()
        {
            return await HelloImpl();
        }

        private Task<string> Hello2()
        {
            return Task.Run(() => "Hello world 2");
        }

        private async Task<String> HelloImpl()
        {
            return await Task.Run(() => "Hello World");
        }
    }
}
4
  • 1
    Stephen Cleary explains the async/deadlock dilemma here: blog.stephencleary.com/2012/07/dont-block-on-async-code.html Commented Dec 16, 2013 at 21:56
  • What's the point of Hello()? It doesn't do anything. Just call HelloImpl directly. Also, there's no point in making a method async just so you can await one task that you return; just make the method not be async and return that task directly. Commented Dec 16, 2013 at 21:56
  • @Servy I could make a complex implementation and all, but I prefered to put a very simple example to understand exactly what is the problem here. But if you want a more complex implementation of exactly that look at asp.net identity framework that is used exactly like that. Commented Dec 16, 2013 at 22:00
  • @Erick I'm describing how you can simplify your code, not telling you to make it more complex... Commented Dec 16, 2013 at 22:01

2 Answers 2

8

The crux of the issue is that await will (by default) capture the current "context" and use that to resume the async method. In ASP.NET, that "context" is a SynchronizationContext that only allows one thread in at a time.

So, when you block the request thread by calling Result, you are blocking a thread within that SynchronizationContext and thus the Hello method cannot resume within that request context.

The reason your Hello2().Result works is that it's not actually an async method; it's just sticking some work on a thread pool thread, which will complete fine independently of the request thread.

I have a blog entry that goes into the full details.

Sign up to request clarification or add additional context in comments.

Comments

1

This is a deadlock. In SyncMethod you are waiting on yourself:

Hello().Result waits on your request's thread.

await HelloImpl() and the await Task.Run(...) inside of HelloImpl() both return execution to your request's thread, but can't because the .Result is blocking it.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.