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.