Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fire and forget an async Task method sometimes not invoke

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)?

like image 751
langtu Avatar asked Mar 26 '14 15:03

langtu


People also ask

What happens if an async method is not awaited?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete.

Why you shouldn't use async void?

Async void methods can wreak havoc if the caller isn't expecting them to be async. When the return type is Task, the caller knows it's dealing with a future operation; when the return type is void, the caller might assume the method is complete by the time it returns. This problem can crop up in many unexpected ways.

Why must async methods return task?

For methods other than event handlers that don't return a value, you should return a Task instead, because an async method that returns void can't be awaited. Any caller of such a method must continue to completion without waiting for the called async method to finish.

Can we use async without task?

Async code can be used for both I/O-bound and CPU-bound code, but differently for each scenario. Async code uses Task<T> and Task , which are constructs used to model work being done in the background. The async keyword turns a method into an async method, which allows you to use the await keyword in its body.


2 Answers

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.

like image 102
Stephen Cleary Avatar answered Oct 13 '22 00:10

Stephen Cleary


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.

like image 28
Dan Bryant Avatar answered Oct 12 '22 22:10

Dan Bryant