HostingEnvironment.QueueBackgroundWorkItem是否能真正延迟应用程序池的回收?

3

我阅读了这份文档,它说使用HostingEnvironment.QueueBackgroundWorkItem运行操作:

与普通的线程池工作项不同的是,ASP.NET可以跟踪通过该API注册的工作项的运行数量,并且ASP.NET运行时将尝试延迟AppDomain关闭,直到这些工作项完成执行。此API不能在非ASP.NET托管的AppDomain外部调用。当应用程序关闭时,提供的CancellationToken将会被发出信号。

所以我编写了这个示例代码:

private void Check() {   
  HostingEnvironment.QueueBackgroundWorkItem(ct => CheckRecyclingBehaviour(ct));}
}

private async void CheckRecyclingBehaviour(CancellationToken ct) {
  while (true) {
    await Task.Delay(1000);
    if (ct.IsCancellationRequested) {
      AppendToFile("Recycling soon...");
      await Task.Delay(1000);
      AppendToFile("But we still have time to finish...");
      break;
    }
  }
}

我已经在IIS 7上运行了 check(),过了一会儿后,我通过IIS管理器强制进行了一次回收。
最后,我检查了 debug.txt 文件,其中只包含了一行内容:“即将回收...”。
所以我猜测 CancellationToken 已经被信号了,但是 AppDomain Shutdown 没有真正延迟(因为第二个打印从未发生)。
这似乎非常奇怪,特别是考虑到我读过的几篇文章都说回收将被延迟30秒
我错过了什么吗?
1个回答

6
您使用了 async void,这就是导致问题的原因。正如我在我的 MSDN 异步最佳实践文章中所述,您应该避免使用 async void。async void 更改为适当的 async Task,您可能会看到它可以正确工作。
更多信息:检测 async void 方法的完成情况并不容易,因此您发布到 QueueBackgroundWorkItem 的代码实际上几乎立即完成(在第一个 await 处)。当 ASP.NET 关闭时,它设置取消标记(同步写入文件的第一行),然后等待任何排队的工作。由于工作已经完成,它只是立即关闭应用程序域,放弃方法的其余部分。
如果您使用的是 async Task 方法,则 QueueBackgroundWorkItem 将理解直到任务完成,代码才算完成。

1
其实我不确定这是否完全解决了问题,我可能是错的,但我认为强制回收并不尊重在QueueBackgroundWorkItem中运行的任务,只有在“自然发生”时才会尊重队列。将其更改为Task将为自然回收修复它,但我仍然认为强制回收将表现相同。 - Scott Chamberlain
1
谢谢,我改成了Task,它起作用了。Scott,感谢你的警告,但幸运的是强制回收确实尊重正在运行的任务。 - oavraham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接