7

I have an async method:

public async Task DoSomethingAsync(){
    ...
    await ...
    await ...
    ....
    return await SaveAsync();
}

In most time, I call this method by:

await DoSomethingAsync()

This call works as expected. But somewhere, I need to call this method as fire and forget:

public void OtherMethod()
{
    ...
    DoSomethingAsync(); //fire and forget here
}

In this case, sometimes the Task DoSomethingAsync() runs and completes, but sometimes the task never invoked (or invoke some awaits within DoSomethingAsync() but never complete the last await SaveAsync();).

I'm trying to make sure the task will be invoked in fire and forget manner by these code:

public void OtherMethod()
{
    ...
    Task.Factory.StartNew(() =>
    {
        await DoSomethingAsync();
    }); //fire and forget again here
}

However, this does not work as expectation. So my questions are:

  1. How to make the call to DoSomethingAsync() without await will always run and complete? (I don't care the case AppDomain restart/crash)

  2. If I remove all async/await code within DoSomethingAsync() and replace await by .ContinueWith(), then the call to Task DoSomethingAsync() (not have async in method declaration) will be invoked and sure to complete (ignore case AppDomain restart/crash), if yes, how long from the call (I don't think that I'll be happy if the Task will be invoked after 10 minutes)?

langtu
  • 1,198
  • 1
  • 10
  • 23

3 Answers3

5

You're probably getting an exception somewhere in DoSomethingAsync, which you cannot observe because you're ignoring the task. This is exactly the behavior you're asking for, since you're telling the code to "forget" the task.

To observe the exception, you cannot "forget" the task:

public Task OtherMethodAsync()
{
  ...
  return DoSomethingAsync();
}

And at some point, await (or Wait) the returned task. That is how you know the task will run and complete.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Ah, I missed the Exception case; I was assuming the OP was hooking [TaskScheduler.UnobservedTaskException](http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.unobservedtaskexception%28v=vs.110%29.aspx), but that's not necessarily a valid assumption. – Dan Bryant Mar 26 '14 at 15:36
  • hmm, actually, the `DoSomethingAsync()`'s body is wrapped by `try catch` and exception is logged if occured. But I did not see any exception generated – langtu Mar 26 '14 at 17:49
  • @langtu: In that case, it could be a deadlock situation as suggested by DanBryant. – Stephen Cleary Mar 26 '14 at 18:01
4

The awaits should be working fine, so there is likely something else going on here that is holding up one or more of your methods being awaited. There are a few possibilities:

  1. You have a deadlock somewhere in one of your methods, preventing it from completing and blocking the await from resuming.
  2. All of your thread pool threads are blocking for some reason, preventing pending Tasks from running.
  3. You're running the async method in a synchronization context and something is holding up the context, preventing it from running the dispatched callbacks. Typically the context would be the UI thread and generally this is pretty obvious, since it locks up your application UI.

If you can attach with the VS debugger and observe what's happening, try pausing and looking at the Parallel Stacks view. This should help narrow down which possibilities to consider.


As Stephen pointed out, it's also possible that an Exception is occurring when you're calling it fire-and-forget. If you're not already, make sure you handle TaskScheduler.UnobservedTaskException to log any events like this. Note that this is called from the finalizer, so the time at which it gets called is non-deterministic. This can make debugging tricky, since the event might not fire until much later than the actual event that caused the exception. As such, I recommend following Stephen's advice and saving the Task to await or Wait somewhere else later.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
2

ASP.NET generally does not allow fire-and-forget (async void) operations to be kicked off from within a request. See https://stackoverflow.com/a/22484832/59641 for a further explanation of this behavior and some potential workarounds.

Community
  • 1
  • 1
Levi
  • 32,628
  • 3
  • 87
  • 88