c# - 为什么调用 IHost.StopAsync 时会调用两次 IHostedService.StopAsync?
问题描述
鉴于以下使用通用主机的 .NET Core 2.2 控制台应用程序:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace SimpleGenericHost
{
class SimpleHostedService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Service started");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Service stopped");
return Task.CompletedTask;
}
}
class Program
{
static async Task Main(string[] args)
{
var host = new HostBuilder()
.ConfigureServices(services =>
{
services.AddHostedService<SimpleHostedService>();
})
.Build();
var runTask = host.RunAsync();
await Task.Delay(5000);
await host.StopAsync();
await runTask;
}
}
}
当你运行它时,输出如下:
Service started
Application started. Press Ctrl+C to shut down.
Hosting environment: Production
Content root path: C:\projects\ConsoleApps\SimpleGenericHost\bin\Debug\netcoreapp2.2\
Service stopped
Service stopped
如您所见SimpleHostedService.StopAsync
,它被调用了两次。为什么?
这是预期的吗?我错过了什么吗?是否有另一种方法可以停止IHostedService.StopAsync
只调用一次的主机?
解决方案
因为它被调用了两次——一次在延迟之后,另一次在服务实际停止时。它并不意味着在使用时被调用RunAsync
。
要在超时后停止,请使用带有超时的 CancellationTokenSource 并将其令牌传递给RunAsync
:
var timeoutCts=new CancellationTokenSource(5000);
await host.RunAsync(timeoutCts.Token);
解释
StopAsync 不会停止服务,它用于通知服务它需要停止。当应用程序停止时,它由托管服务基础架构本身调用。
.NET Core 是开源的,这意味着您可以查看RunAsync 源代码。RunAsync
启动主机,然后等待终止:
await host.StartAsync(token);
await host.WaitForShutdownAsync(token);
WaitForShutdownAsync方法侦听来自控制台或显式调用的终止请求IHostApplicationLifetime.StopApplication
。当这种情况发生时,它会调用StopAsync
自己:
await waitForStop.Task;
// Host will use its default ShutdownTimeout if none is specified.
await host.StopAsync();
如果您打算自己管理应用程序的生命周期,则应该使用StartAsync
而不是。RunAsync
但在这种情况下,您只需要在应用程序超时时停止它。您可以通过CancellationTokenSource(int)构造函数将取消令牌传递给RunAsync
仅在超时后触发的事件来轻松做到这一点:
var timeoutCts=new CancellationTokenSource(5000);
await host.RunAsync(timeoutCts.Token);
推荐阅读
- r - 通过 as.numeric 将 .tsv 列强制转换为数字时,如何避免“NA”值?
- python - 跟踪导入 Python 模块的时间
- sql-server - 对于仅在过去 24 小时内运行的日志,如何返回过去 x 个月的平均处理时间
- discord.js - 创建频道权限覆盖
- django - 如何根据请求在 ModelAdmin 类上设置一些初始属性?
- encryption - 是否可以编写一个可以使用所有字母数字作为输入但不输出歧义字符的 Enigma 加密算法?
- laravel - 如何从 POST 接收 JSON?
- python - HTML 未应用 CSS
- ios - 将 Swift ViewController 导入到 React Native
- javascript - 从 php 数组到 javascript(初学者)