2

假设我有以下异步方法需要相当长的时间才能完成其工作:

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";
   }

Q1:方法#1 和#2 有什么区别?

Q2:在 ASP.NET 世界中,运行方法的正确方法是什么(在本例中,LongWork() 在后台线程中包含一些异步/等待对?特别是在#3 中,没有“等待”在 Task.Factory.StartNew(async () => await LongWork()) 之前。可以吗?

谢谢!

4

1 回答 1

10

Q1:方法#1 和#2 有什么区别?

#1 的开销更少。这是唯一的区别。

Q2:在 ASP.NET 世界中,在后台线程中运行一个方法(在本例中,LongWork() 包含一些异步/等待对)的正确方法是什么?

您提供的选项都没有。一方面,他们都Task.Factory.StartNew没有指定 a 的情况下使用TaskScheduler,这是危险的(正如我在博客中所描述的那样)。他们应该Task.Run改用。但是,即使使用Task.Run,也会遇到更严重的潜在问题。

根本问题是这样的:HTTP 协议以每个请求为中心,每个请求都有一个匹配的响应。当 HTTP 服务器(例如 ASP.NET)知道没有未完成的请求时,它会做出类似“回收工作进程是安全的”的假设。

我在我的博客上更详细地描述了这个问题。在那篇博文中还有一种类型BackgroundTaskManager,它向 ASP.NET 运行时注册后台任务并(正确地)通过Task.Run. 只有在阅读了博客文章并理解并接受这仍然是危险和不安全的情况下,您才应该使用。BackgroundTaskManager

一个更好(阅读:更可靠)的解决方案是首先将要完成的工作的表示写出到持久存储(例如,Azure 队列)并拥有一个处理请求的独立后端进程(例如,Azure 工作者角色)从队列中。

于 2013-10-02T23:32:19.013 回答