消防并忘记异步任务与任务。运行

时间:2016-03-08 02:07:08

标签: c# asp.net multithreading asynchronous async-await

我在开发一些ASP.Net应用程序时看到了一些空引用问题。例外情况如下:

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

在搜索Google和SO之后,很可能是因为我的一些代码会以这种方式触发并忘记异步任务:

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.
    }
}

我认为它与此问题"Fire and forget async method in asp.net mvc"非常相似,但并不完全相同,因为我的代码不在控制器上并接受请求。我的代码专门用于运行后台任务。

正如上面的SO问题一样,通过将代码更改为下面的问题修复了问题,但我只是想知道为什么以及从任务返回的异常任务和任务有什么区别。 :

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.
    }
}

从我的角度来看,这两个代码只创建了一个任务而忘了它。唯一的区别是第一个没有等待任务。这会导致行为上的差异吗?

我从其他SO问题中了解了火灾和遗忘模式的局限性和不良影响。

1 个答案:

答案 0 :(得分:13)

  

我只是想知道为什么以及从任务返回的异步任务和任务有什么不同

区别在于是否捕获了AspNetSynchronizationContext,这是&#34;请求上下文&#34; (包括当前文化,会议状态等)。

直接调用时,Start在该请求上下文中运行,默认情况下await将捕获该上下文并在该上下文中继续(更多信息on my blog)。这可能导致有趣的&#34;行为,如果,例如,Start尝试在请求上下文中恢复已完成的请求。

使用Task.Run时,Start在没有请求上下文的其他线程池线程上运行。因此,await将不会捕获上下文,并将在任何可用的线程池线程上恢复。

我知道你已经知道这一点,但我必须为Google员工重申:请记住,以这种方式排队的任何工作都不可靠。至少,您应该使用HostingEnvironment.QueueBackgroundWorkItem(或AspNetBackgroundTasks library,如果您还没有使用4.5.2)。这些都非常相似;他们会将后台工作排队到线程池(事实上,我的库BackgroundTaskManager.Run does call Task.Run),但这两个解决方案还将采取与ASP协同工作的注册的额外步骤.NET运行时。这不足以使背景工作变得可靠&#34;从真正意义上讲,它确实最小化你失去工作的机会。

相关问题