gpt4 book ai didi

c# - 触发并忘记异步任务与 Task.Run

转载 作者:太空宇宙 更新时间:2023-11-03 17:16:57 25 4
gpt4 key购买 nike

我在开发某些 ASP.Net 应用程序时遇到一些空引用问题。异常(exception)情况如下:

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" ,但并不完全相同,因为我的代码不在 Controller 上并接受请求。我的代码专用于运行后台任务。

与上面的 SO 问题一样,通过将代码更改为下面的代码解决了这个问题,但我只想知道为什么以及从 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.
}
}

在我看来,这两种代码都只创建了一个任务,然后忘记它。唯一的区别是第一个任务没有等待。这会导致行为差异吗?

我从另一个 SO 问题中了解到火后遗忘模式的局限性和不良影响。

最佳答案

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

区别在于是否捕获AspNetSynchronizationContext,即“请求上下文”(包括当前文化、 session 状态等)。

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

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

我知道您已经知道这一点,但我必须向 Google 员工重申:请记住,任何以这种方式排队的工作都是不可靠的。至少,您应该使用 HostingEnvironment.QueueBackgroundWorkItem(或者我的 AspNetBackgroundTasks library 如果您还没有使用 4.5.2)。这些都非常相似;它们会将后台工作排队到线程池(实际上,我的库的 BackgroundTaskManager.Run does call Task.Run ),但是这两种解决方案还将采取额外的步骤,即 注册 与 ASP.NET 运行时一起工作。这不足以使后台工作在真正意义上“可靠”,但它确实可以最大限度地减少您丢失工作的可能性。

关于c# - 触发并忘记异步任务与 Task.Run,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35857628/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com