假设我有以下异步方法,需要相当长的时间才能完成其工作:
void async Task LongWork()
{
await LONGWORK() // ... long work
}
现在,在web api中,我想在后台运行该工作(即,我想在启动LongWork()之后但在完成之前返回Http请求:
我可以想到实现这个目标的三种方法:
1) public async Task<string> WebApi()
{
... // do another work
await Task.Factory.StartNew(() => LongWork());
return "ok";
}
2) public async Task<string> WebApi()
{
... // do another work
await Task.Factory.StartNew(async () => await LongWork());
return "ok";
}
3) public async Task<string> WebApi()
{
... // do another work
Task.Factory.StartNew(async () => await LongWork());
return "ok";
}
问题1:方法#1和#2之间有什么区别?
Q2:在ASP.NET世界中,运行方法(在此示例中,LongWork()在后台线程中包含一些异步/等待对的正确方法是什么? 特别是在#3中,在Task.Factory.StartNew(async()=&gt; await LongWork())之前没有“等待”。好吗?
谢谢!
答案 0 :(得分:8)
问题1:方法#1和#2之间有什么区别?
#1的开销较小。这是唯一的区别。
Q2:在ASP.NET世界中,运行方法(在此示例中,LongWork()在后台线程中包含一些异步/等待对的正确方法是什么?
您提供的所有选项均不适用。首先,他们都是use Task.Factory.StartNew
without specifying a TaskScheduler
, which is dangerous(正如我在博客中描述的那样)。他们应该使用Task.Run
代替。但是,即使您使用Task.Run
,也会遇到更严重的潜在问题。
基本问题是:HTTP协议以每个请求为中心,只有一个匹配的响应。当HTTP服务器(例如ASP.NET)知道没有未完成的请求时,它会做出“回收工作进程是安全的”假设。
I describe this problem in more detail on my blog。此博客文章中还有一个类型BackgroundTaskManager
,它使用ASP.NET运行时注册后台任务,并通过Task.Run
(正确)执行它们。 如果您阅读博客文章并理解并接受这仍然存在危险且不安全,则应该只使用BackgroundTaskManager
。
更好(读取:更可靠)的解决方案是首先写出要对持久存储(例如,Azure队列)执行的工作的表示,并且具有独立的后端进程(例如,Azure工作者角色)处理来自队列的请求。