9

I'm using Visual Studio 2012 RC with .Net 4.5 and ASP MVC 4 RC. It hangs whenever I use async at all. The controller action method uses async but is not itself an async controller method.

There are no errors logged or exceptions thrown, but the browser shows "Waiting for www.myweb.local" forever.

// Simplest possible async
public class Waiter
{
    public async Task<int> GetValue()
    {
        await Task.Yield();
        return await Task.Factory.StartNew(() => 42);
    }
}

// simplest possible controller that uses the async
public class HomeController : Controller

    public ActionResult Index()
    {
        var waiter = new Waiter();
        var resultTask = waiter.GetValue();
        int result = resultTask.Result;

        // it never gets here 
        return View();
    }
}

I have done the things that are noted in this answer, and it still does not work. ie. The web.config contains

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>

And the magic words await Task.Yield(); are in the async method.

The .Net framework version is 4.5.50501. I have observed this behaviour on IIS Express and on IIS 6.0.

I tried applying the "July 2012 update" to VS2012, but that did not fix it.

This answer suggests that it may be because the task is already completed when I wait for it, however if that was the case, this should work and it does not:

public class Waiter
{
    public async Task<int> GetValue()
    {
        await Task.Yield();
        return await Task.Factory.StartNew(() => 
            {
                Thread.Sleep(1500);
                return 42;
            });
    }
}

A couple of people have suggested that ConfigureAwait(false) is needed, but this code does not work either:

    public async Task<int> GetValue()
    {
        var task = new Task<int>(() => 42);
        return await task.ConfigureAwait(false);
    }

The following does work with the razor view engine, but not with spark. Surely there should be a way to get the other scenario working as well? Can one not use the async Tasks inside synchronous code?

public class Waiter
{
    public async Task<int> GetValue()
    {
        return await Task.Factory.StartNew(() => 42);
    }
}

public class HomeController : Controller
{
    public async Task<ActionResult> IndexAsync()
    {
        await Task.Yield();
        var waiter = new Waiter();
        int result = await waiter.GetValue();

        return View();
    }
}

I know that is this isn't released software, but Microsoft's RCs are usually quite stable, so I'm surprised that it fails, and fails in an unhelpful way.

Community
  • 1
  • 1
Anthony
  • 5,176
  • 6
  • 65
  • 87
  • 2
    Sorry if I'm missing something, but why have the Index block via accessing the Result property instead of marking it async and using await there? If that's intentional, you might need to use ConfigureAwait(false) so the continuation doesn't schedule back on the same thread. – James Manning Jul 06 '12 at 15:12
  • I'm testing porting an existing website to .Net 4.5. It uses the spark view engine. I wanted to get async working before I dealt with async controllers and whether spark could handle Task or not (my initial impression was no). But regardless of whether I could do that or not, this scenario should work. – Anthony Jul 06 '12 at 15:23
  • Why are you deriving from AsyncController? That's not necessary. See my async sample http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4 – RickAndMSFT Jul 07 '12 at 20:15
  • Thanks for that tip Rick, I have modified the code e.g. to fit. – Anthony Jul 07 '12 at 23:32

2 Answers2

11

You are causing a deadlock, just like this question.

James Manning's suggestion is correct, but you have to await the result of ConfigureAwait, like this:

public async Task<int> GetValue()
{
    var task = new Task<int> (() => 42);
    return await task.ConfigureAwait(false);
}

In general, mixing synchronous and asynchronous code is a Really Bad Idea unless you Really Know what you're doing. Making the controller action asynchronous would be much better.

Community
  • 1
  • 1
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 1
    I agree - particularly as it's so easy now to make the controller methods asynchronous. Although I can see some (very *very* rare) cases where it might make sense to run some tasks in parallel within a synchronous handler. Always worth a read of this article before getting carried away with async controllers too: http://blog.stevensanderson.com/2010/01/25/measuring-the-performance-of-asynchronous-controllers/ – James World Jul 06 '12 at 21:18
  • Surely any c# program that uses the async keyword "mixes synchronous and asynchronous code"? Using an Async controller just means that the boundary is in the framework. – Anthony Jul 06 '12 at 22:31
  • Regarding "it's so easy now to make the controller methods asynchronous" - not if you have a lot of existing views using the spark view engine. – Anthony Jul 06 '12 at 22:44
  • 1
    @Anthony: What I mean by "mixing" is that there is an event controller for every app (UI apps have a message pump; ASP.NET apps have a request context). Extending `async` all the way to that event controller is the cleanest and easiest design. You don't want to "mix" in the sense of having a synchronous event handler call into an `async` layer. – Stephen Cleary Jul 06 '12 at 23:19
  • 1
    I've never used spark, but if it's just a view engine, can't you make your controller methods `async` anyway? – Stephen Cleary Jul 06 '12 at 23:19
  • Stephen - from having used spark, it appears not. I.e. you can, but it doesn't work in any meaningful way. View engines typically deal with data whose types they don't know how to handle by doing a ToString() on it. This is better than nothing, but fails to do anything useful with Task (.e. just emits the type name, does not render any data). – Anthony Jul 07 '12 at 09:29
  • Pretty clear that spark doesn't work with these yet: https://groups.google.com/forum/?fromgroups#!topic/spark-dev/eyIFm6mnBcE – Anthony Jul 07 '12 at 10:02
  • Also, have you tried the "return await task.ConfigureAwait(false)" - doesn't work for me. – Anthony Jul 07 '12 at 10:47
  • You also have to remove the call to `Yield` if you have a synchronous controller calling into the `async` layer. – Stephen Cleary Jul 07 '12 at 12:14
  • I already did that, as per your code above. Does it work for you? Doesn't work for me. – Anthony Jul 07 '12 at 14:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13553/discussion-between-anthony-and-stephen-cleary) – Anthony Jul 07 '12 at 15:02
1

I have had async working in the beta just fine. I've not tested it, but I'm guessing it's because your controller method is not async. Change it to this:

public async Task<ActionResult> IndexAsync()
{
    var waiter = new Waiter();
    int result = await waiter.GetValue();

    // it never gets here 
    return View();
}
James World
  • 29,019
  • 9
  • 86
  • 120
  • If it's "because the controller method is not async" are you saying that using async tasks will never work in sync code? Otherwise, there should be a way to get this code working. – Anthony Jul 06 '12 at 15:51
  • So GetValue() not completing then... I can't think of an obvious reason for that off the top of my head from what you've got and I'm on the train right now! I'll have a look later if no one's beaten me to it. – James World Jul 06 '12 at 15:57
  • What happens if you access result (do something with it) after the await? – James World Jul 06 '12 at 15:58