8

In my ASP.NET MVC 4 application, say I have some async method:

async Task DoSomeBackgroundWork()
{
   // Fake it
   await Task.Delay(5000);
}

Now, one can call this method like this:

Task.Run(async () => 
{
  await DoSomeBackgroundWork();
  Debug.WriteLine("Background work finished");
});

or this:

DoSomeBackgroundWork()
  .ContinueWith(t => Debug.WriteLine("Background work finished"));

Note that in both versions the caller doesn't wait for the background task to finish. It's just fire and forget.

While both versions work identical (ignore exception handling here) on regular .NET apps, on a MVC app, when calling the code from a MVC controller method, Debug.WriteLine is only executed in the Task.Run version - but not in the ContinueWith version.

Can anyone shed some light onto why these two versions are working differently when called in a MVC controller method?

noseratio
  • 59,932
  • 34
  • 208
  • 486
Sebastian Krysmanski
  • 8,114
  • 10
  • 49
  • 91
  • Works fine for me too. Popped into `Global.asax` in a .NET4.5 Web App – Xenolightning Apr 15 '14 at 09:31
  • @Noseratio It actually currently does look that way. – Sebastian Krysmanski Apr 15 '14 at 09:36
  • 2
    Add `Debug.WriteLine(new { SynchronizationContext.Current });` inside and outside `DoSomeBackgroundWork`, everywhere. Do you see `AspNetSynchronizationContext` or `LegacyAspNetSynchronizationContext` anywhere? (what and where exactly?) – noseratio Apr 15 '14 at 09:40
  • @SriramSakthivel Thats the point! In Console it works fine for instance. But not in MVC. – Alexander Schmidt Apr 15 '14 at 09:50
  • 1
    @Noseratio Inside the controller method (caller), the context is `AspNetSynchronizationContext`. Inside of `DoSomeBackgroundWork()`, it's also `AspNetSynchronizationContext` when calling it directly from the controller method; and it's `null` when `DoSomeBackgroundWork()` is called from within `Task.Run()`. – Sebastian Krysmanski Apr 15 '14 at 09:51
  • @sprinter252 but no where in question OP said it works in Console App. – Sriram Sakthivel Apr 15 '14 at 09:58
  • I tried out `new HttpClient().GetAsync(url).ContinueWith(request => { Debug.WriteLine("Getting content."); });`and this is writing it to the output. I think the problem is inside the async task which is called. – Alexander Schmidt Apr 15 '14 at 10:07

1 Answers1

5

This is probably something specific to your web app, so I can only speculate, but here's what I think:

  • It should work if you do this:

    DoSomeBackgroundWork()
        .ContinueWith(t => Debug.WriteLine("Background work finished"), 
            TaskContinuationOptions.ExecuteSynchronously);
    

    This would be more close to the Task.Run version, where the continuation runs synchronously.

  • Right before you call DoSomeBackgroundWork directly from the controller method, add this:

    Debug.WriteLine(new { TaskScheduler.Current });
    

    If the output is anything but ThreadPoolTaskScheduler, I might be able to further explain this behavior (as I've just dealt with a somewhat related problem here).

BTW, using fire-and-forget in ASP.NET like this is really not a good idea, there's a lot of questions on SO discussing this.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486