【发布时间】:2021-05-22 08:30:17
【问题描述】:
我们正在开发一个单一的 Web 应用程序——非常有状态。它处理 HTTP 请求和长期存在的 SignalR 连接。 (在 ASP.NET Core 3.1 中——我们稍后会升级到 .NET 5。)
我们从登录页面重定向到我们的“主页”。主页需要一段时间来加载和初始化,然后它会与 SignalR 连接。我们在服务器端还有很多工作要做。在登录请求中执行服务器工作(在重定向到主页之前)会减慢登录速度。
“哦,那我们就用任务吧!”,我想。也就是说,将服务器工作放在一个Task中,将其保存在用户状态中,并让它与主页面的加载并行执行。像这样(简化):
public static async Task ServerSideInit()
{
// do a lot of init work
}
// at the end of the controller handling the login page POST:
UserState.BackgroundTask = ServerSideInit();
Redirect(UrlToTheMainPage);
// when the main page connects via SignalR:
try {
await UserState.BackgroundTask;
}
catch {
// handle errors in the init work
}
这确实会加快速度。页面加载或初始化工作是否先完成并不重要——我们等待任务。 ServerSideInit() 中的工作并不重要。如果发生某些事情并且主页永远无法连接,则 UserState(和任务)将在超时后被销毁——这完全没问题。 (有一些注意事项。例如,我们必须使用 IServiceProvider 在 ServerSideInit() 中创建/处置范围,因此我们在控制器之外获得了范围 DbContext。但这没关系。)
然后我读到,ASP.NET Core 框架在结束 POST 请求时存在关闭任务的风险! (Do you have to await async methods?) 简单的 HostingEnvironment.QueueBackgroundWorkItem 不再可用。不过,有一个新的 BackgroundService 类。 (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio) 但是注册服务和排队作业似乎是一个非常麻烦的解决方案……我们只想触发一个需要几秒钟才能完成的任务,并在 ASP.NET Core 完成处理后让它继续运行POST 请求。
我对 ASP.NET Core 不是很有经验……所以我非常感谢您提供一些意见!我的简单解决方案行不通吗?任务会被框架终止吗?有没有更简单的方法告诉框架“请不要碰这个任务”?或者 BackgroundService 是要走的路?
【问题讨论】:
-
该任务是静态的还是动态的?意思是,它对所有用户都是一样的还是不同的?
-
一旦您重定向您返回 HTTP 响应,服务器将停止处理。所以你的所作所为没有多大意义。如果你想要“一劳永逸”,你应该使用
Task.Run()来生成一个新线程,而不是async -
“并行执行”异步和并行是不同的东西
-
这是一篇关于这个主题的必读文章:Fire and Forget on ASP.NET
标签: c# asp.net asp.net-core async-await task-parallel-library