如果没有应用程序回收,那么在 ASP.NET 中使用后台工作人员有好处吗?

iUk*_*nwn 5 c# asp.net multithreading .net-core asp.net-core

背景:我有一个简单的 ASP.NET Core 3.1 网站。在极少数情况下(每周三到四次),用户可能会填写触发发送电子邮件的表单。

我不想在运行“发送电子邮件”操作时延迟页面响应(即使只需要一两秒),因此从我读过的所有内容来看,处理电子邮件的代码似乎应该是后台工作程序/托管服务,并且 Razor 页面代码应将要发送的数据对象放置在由后台服务监控的集合中。

我不完全理解为什么这在现代ASP.NET Core 中是必要的。

如果我在普通的 C# 应用程序(而不是 ASP)中执行此操作,我只需将“发送电子邮件”方法设置为异步(它使用 MailKit,它具有异步方法),然后调用异步方法而不等待,从而允许工作在线程池上完成,同时允许响应线程继续。

但现有的答案和博客文章表示,在 ASP 中调用不带等待的异步方法是危险的,因为 IIS 可以重新启动 ASP 进程(应用程序池回收)。

然而,我读过的大多数内容都说应用程序回收是旧 ASP 的产物,当时内存泄漏很常见,而它在 .Net Core 上并不是真正的东西。此外,许多 ASP 应用程序甚至不再托管在 IIS 中。

此外,据我所知,IHostedService/Background Worker 对象没有做任何特殊的事情 - 它们似乎没有添加任何额外的线程;它们看起来就像单例,具有环境启动和关闭的附加通知。

所以:

  • 在 ASP.NET Core 中调用“即发即弃”异步方法是否仍然被认为是不好的做法,特别是如果“即发即弃”任务是短暂的?如果是这样,为什么?[请参阅下面的编辑以获取澄清]
  • 除了关闭通知之外,还有什么理由认为后台服务比借用托管线程池线程(通过 Task.Run 或 QueueBackgroundWorkItem)更好?唤醒后台服务(如果它正在等待将对象放入集合中)不会以相同的方式消耗池线程吗?

编辑:我承认,当操作有可能终止时,启动任务并向用户报告成功是很糟糕的形式。收到关闭通知并能够完成任务是有好处的。

也许更好的问题是,旧的循环行为是否仍然存在于现代 ASP 中(在 IIS 或 Kestrel 上)?是否还有其他原因可能触发有序关闭(服务器关闭/手动停止除外)?

wei*_*hch 3

我仍然认为这是一种糟糕的做法。

这里以及参考帖子中的主要关注点主要是关于任务完成的承诺。

如果不知道幽灵后台任务,运行时将无法通知任务正常停止。这可能会或可能不会导致严重问题,具体取决于终止发生时任务的状态。

使用火忘记任务通常意味着,当进程重新启动时,您的任务面临着必须重新开始的风险。有时,由于上下文丢失,这是不可能的。想象一下,您的即忘任务正在使用 Web 请求提供的参数调用另一个 Web API。如果进程重新启动,这些参数可能会从内存中删除。

请记住,回收并不总是由 IIS/服务器触发。它也可能是由人触发的。假设当您的应用程序遇到内存泄漏问题时,您可能希望每 1 小时回收一次应用程序进程作为临时缓解措施。然后您需要确保不会破坏后台任务。

在托管方面- 仍然可以在进程中托管 ASP.Net Core 应用程序,其中应用程序池在配置的时间段(默认为 29 小时)后被 IIS 回收。

就生命周期而言- 托管服务是您注册到 DI 的类型,因此可以使用 DI 功能,例如,此内置托管服务实现了IDisposable,这意味着可以在关闭时进行适当的清理。

坦率地说,后台任务和托管服务都可以让您做到“一劳永逸”。但当您需要可靠性和弹性时,托管服务会获胜。