Windows 使用.net core BackgroundService的服务,如何优雅停止?
Windows Service using .net core BackgroundService, how to gracefully stop?
我正在使用 .Net Core 3.1
开发 windows 服务
很多在线资源建议我使用 BackgroundService
。问题是我无法通过重写 ExecuteAsync
方法来优雅地停止服务。
测试代码设置非常简单:
public class MyService: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine($"Service started at {DateTimeOffset.Now}");
await DoLongRunningWork(stoppingToken);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Service is being stopped...");
await base.StopAsync(cancellationToken);
Console.WriteLine("Service stopped at {DateTimeOffset.Now}");
}
private static async Task DoLongRunningWork(CancellationToken token)
{
Console.WriteLine("I'm working...");
while (!token.IsCancellationRequested)
{
Console.WriteLine($"\t... doing work ...");
await Task.Delay(1000, token);
}
// THE BELOW LINE IS NEVER REACHED. i.e The line "Cancel requested? True" is never logged out...
Console.WriteLine($"Cancel requested? {token.IsCancellationRequested}");
}
}
和Main
方法
static void Main(string[] args)
{
Console.WriteLine("Starting My Service...");
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices(services =>
{
services.AddHostedService<MyService>();
})
.Build()
.Run();
}
我能做的一件事是实现我自己的 Stop
方法并在我的覆盖 StopAsync
中调用它。但我很好奇为什么这个简单的设置没有按预期工作。
我已经阅读了 BackgroundService.cs 源代码,它看起来应该按照我预期的方式工作(即这一行 Cancel requested? True
应该被注销,但它没有...
我已经尝试 运行 它作为控制台应用程序并使用 Ctrl+C
停止它,以及将它安装为 Windows 服务并使用 service.msc
来控制它。相同的行为(即没有正常关闭...)
非常感谢任何帮助!
我忘记了这么久了。当我回过头来时,我发现了问题......这只是一个混乱
这一行
await Task.Delay(1000, token);
将抛出 TaskCancelledException
因此循环将退出,方法将停止... @Jeremy Lakeman 对此发表了评论,但当时我没有想通,我猜...
我只需要小心使用带有取消标记的 await Task.Delay(..., token)
...
所以,我创建了一个简单的扩展程序
public static async Task SafeDelay(this Task source)
{
try
{
await source;
}
catch (TaskCanceledException)
{
// I don't want a throw on TaskCanceledException...
}
}
private static async Task DoLongRunningWork(CancellationToken token)
{
Console.WriteLine("I'm working...");
while (!token.IsCancellationRequested)
{
Console.WriteLine($"\t... doing work ...");
// I simply want the loop to keep running, I don't want an absurd stop...
await Task.Delay(1000, token).SafeDelay();
}
// This line is now reached...
Console.WriteLine($"Cancel requested? {token.IsCancellationRequested}");
}
我正在使用 .Net Core 3.1
开发 windows 服务很多在线资源建议我使用 BackgroundService
。问题是我无法通过重写 ExecuteAsync
方法来优雅地停止服务。
测试代码设置非常简单:
public class MyService: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine($"Service started at {DateTimeOffset.Now}");
await DoLongRunningWork(stoppingToken);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Service is being stopped...");
await base.StopAsync(cancellationToken);
Console.WriteLine("Service stopped at {DateTimeOffset.Now}");
}
private static async Task DoLongRunningWork(CancellationToken token)
{
Console.WriteLine("I'm working...");
while (!token.IsCancellationRequested)
{
Console.WriteLine($"\t... doing work ...");
await Task.Delay(1000, token);
}
// THE BELOW LINE IS NEVER REACHED. i.e The line "Cancel requested? True" is never logged out...
Console.WriteLine($"Cancel requested? {token.IsCancellationRequested}");
}
}
和Main
方法
static void Main(string[] args)
{
Console.WriteLine("Starting My Service...");
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices(services =>
{
services.AddHostedService<MyService>();
})
.Build()
.Run();
}
我能做的一件事是实现我自己的 Stop
方法并在我的覆盖 StopAsync
中调用它。但我很好奇为什么这个简单的设置没有按预期工作。
我已经阅读了 BackgroundService.cs 源代码,它看起来应该按照我预期的方式工作(即这一行 Cancel requested? True
应该被注销,但它没有...
我已经尝试 运行 它作为控制台应用程序并使用 Ctrl+C
停止它,以及将它安装为 Windows 服务并使用 service.msc
来控制它。相同的行为(即没有正常关闭...)
非常感谢任何帮助!
我忘记了这么久了。当我回过头来时,我发现了问题......这只是一个混乱
这一行
await Task.Delay(1000, token);
将抛出 TaskCancelledException
因此循环将退出,方法将停止... @Jeremy Lakeman 对此发表了评论,但当时我没有想通,我猜...
我只需要小心使用带有取消标记的 await Task.Delay(..., token)
...
所以,我创建了一个简单的扩展程序
public static async Task SafeDelay(this Task source)
{
try
{
await source;
}
catch (TaskCanceledException)
{
// I don't want a throw on TaskCanceledException...
}
}
private static async Task DoLongRunningWork(CancellationToken token)
{
Console.WriteLine("I'm working...");
while (!token.IsCancellationRequested)
{
Console.WriteLine($"\t... doing work ...");
// I simply want the loop to keep running, I don't want an absurd stop...
await Task.Delay(1000, token).SafeDelay();
}
// This line is now reached...
Console.WriteLine($"Cancel requested? {token.IsCancellationRequested}");
}