简体   繁体   中英

Fire and forget async Task vs Task.Run

I'm seeing some null reference issue when developing some ASP.Net application. The exception is as this below:

Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException
Stack:
   at System.Threading.Tasks.AwaitTaskContinuation.<ThrowAsyncIfNecessary>b__1(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

After searching through Google and SO, it was very likely caused by some of my code fire and forget async Task in this fashion:

public interface IRepeatedTaskRunner
{
    Task Start();
    void Pause();
}

public class RepeatedTaskRunner : IRepeatedTaskRunner
{
    public async Task Start() //this is returning Task
    {
        ...
    }
}

public class RepeatedTaskRunnerManager{
    private IRepeatedTaskRunner _runner;
    public void Foo(){
        _runner.Start(); //this is not awaited.
    }
}

I think it is very similar to this question "Fire and forget async method in asp.net mvc" , but is not completely the same, as my code is not on controller and accepting requests. My code is dedicated for running background tasks.

As the same in the above SO question, by changing the code to this below fixed the issue, but I just want to know why and what is the difference with fire-and-forgetting async Task and task returned from Task.Run:

public interface IRepeatedTaskRunner
{
    Task Start();
    void Pause();
}

public class RepeatedTaskRunner : IRepeatedTaskRunner
{
    public Task Start() // async is removed.
    {
        ...
    }
}

public class RepeatedTaskRunnerManager{
    private IRepeatedTaskRunner _runner;
    public void Foo(){
        Task.Run(() => _runner.Start()); //this is not waited.
    }
}

From my point of view, both code just create one task and forget about it. The only difference is the first one there is no await on the Task. Would that cause the difference in behavior?

I'm aware of the limitation and bad effect of the fire and forget pattern as from the other SO question.

I just want to know why and what is the difference with fire-and-forgetting async Task and task returned from Task.Run

The difference is whether the AspNetSynchronizationContext is captured, which is the "request context" (including such things as the current culture, session state, etc).

When directly invoking, Start is run in that request context, and await by default will capture that context and resume on that context (more info on my blog ). This can cause "interesting" behavior, if, say, Start attempts to resume on a request context for a request that has already been completed.

When using Task.Run , Start is run on a different thread pool thread that does not have a request context. So, await will not capture a context and will resume on any available thread pool thread.

I know you already know this, but I must reiterate for Googlers: Remember that any work queued this way is not reliable. At the very least, you should be using HostingEnvironment.QueueBackgroundWorkItem (or my AspNetBackgroundTasks library if you're not on 4.5.2 yet). These are both very similar; they will queue the background work to the thread pool (in fact, my library's BackgroundTaskManager.Run does call Task.Run ), but both solutions will also take the extra step of registering that work with the ASP.NET runtime. This isn't enough to make the background work "reliable" in a true sense of the word, but it does minimize the chances that you'll lose the work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM