任务完成后 IHost 不返回
IHost not returning when Task completed
我正在编写 Windows 服务(使用 .NET Core 3.1),使用 BackgroundService
,当我想以编程方式停止服务时,我似乎遇到了问题。
我的主要功能如下
static async Task Main(string[] args)
{
IHost host = null;
try
{
host = CreateService(args).Build();
await host.RunAsync();
Console.WriteLine("Ending");
}
catch (Exception ex)
{
Console.WriteLine("Exception");
}
finally
{
if (host is IAsyncDisposable d) await d.DisposeAsync();
}
}
public static IHostBuilder CreateService(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "My Service";
})
.ConfigureServices((hostContext, services) =>
{
IConfiguration configuration = hostContext.Configuration;
ServiceOptions options = configuration.GetSection("ServiceOptions").Get<ServiceOptions>();
WorkerService sService = new WorkerService();
services.AddSingleton(options);
services.AddSingleton(sService);
services.AddHostedService<WindowsBackgroundService>(service => new WindowsBackgroundService(
service.GetService<WorkerService>(),
service.GetService<ILogger<WindowsBackgroundService>>(),
service.GetService<ServerOptions>()
));
});
后台服务如下:
public sealed class WindowsBackgroundService : BackgroundService
{
private WorkerService _workerService;
private ServerOptions _options;
private ILogger<WindowsBackgroundService> _logger;
public WindowsBackgroundService(
WorkerService workerService, ILogger<WindowsBackgroundService> logger,
ServiceOptions serviceOptions) => (_workerService, _logger, _options) = (workerService, logger, serviceOptions);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
bool allDone = false;
while (!stoppingToken.IsCancellationRequested && !allDone)
{
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
// Log to watchdog
// Check if we are in the run window
if (_options.InActivePeriod())
{
Console.WriteLine("Process Queue");
var processResult = _workerService.Process(_options);
if (!processResult && _workerService.FatalError)
{
_workerService = null;
allDone = true;
Console.WriteLine("Service Quitting");
}
}
}
Console.WriteLine($"Task Ending {stoppingToken.IsCancellationRequested}");
return;
}
}
}
所以一切 运行 都应该如此,我 运行 在调试器或命令行下进行此操作(我还没有实际将其安装为 Windows 服务,因为我还在写代码)。
函数 Process
正确执行。如果它遇到无法恢复的错误,它会将它设置为 FatalError
属性,这应该是一个信号,表明整个服务应该停止。任务确实完成了(正确的行被写入控制台)但是行 Console.WriteLine("Ending");
永远不会执行。看起来 host.RunAsync();
永远不会 returns(除非我按 CTRL+C)。
我现在不完全确定我做错了什么。谁能指出我的写作方向?
根据显示的代码,我没有看到任何会导致主机停止的东西。
托管服务,一旦启动与应用程序主机本身无关。所以即使ExecuteAsync
完成了,那个功能的完成也不代表宿主会停止运行.
您可以考虑将 IHostApplicationLifetime
注入托管服务并明确告诉应用程序以编程方式停止;
例如
public sealed class WindowsBackgroundService : BackgroundService {
private WorkerService _workerService;
private ServerOptions _options;
private ILogger<WindowsBackgroundService> _logger;
private IHostApplicationLifetime lifetime;
public WindowsBackgroundService(
WorkerService workerService, ILogger<WindowsBackgroundService> logger,
ServiceOptions serviceOptions, IHostApplicationLifetime lifetime)
=> (_workerService, _logger, _options, this.lifetime) = (workerService, logger, serviceOptions, lifetime);
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
bool allDone = false;
while (!stoppingToken.IsCancellationRequested && !allDone) {
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
// Log to watchdog
// Check if we are in the run window
if (_options.InActivePeriod()) {
Console.WriteLine("Process Queue");
var processResult = _workerService.Process(_options);
if (!processResult && _workerService.FatalError) {
_workerService = null;
allDone = true;
Console.WriteLine("Service Quitting");
lifetime.StopApplication(); //SIGNAL APPLICATION TO STOP
}
}
}
Console.WriteLine($"Task Ending {stoppingToken.IsCancellationRequested}");
return;
}
}
我也很好奇您是如何配置服务的。如果默认注册完成,您手动解决的所有问题都会自动注入,为什么还要使用工厂委托?
//...
.ConfigureServices((hostContext, services) => {
IConfiguration configuration = hostContext.Configuration;
ServiceOptions options = configuration.GetSection("ServiceOptions").Get<ServiceOptions>();
services.AddSingleton(options);
services.AddSingleton<WorkerService>();
services.AddHostedService<WindowsBackgroundService>();
});
Host.CreateDefaultBuilder
为您添加了日志记录和主机生命周期
我正在编写 Windows 服务(使用 .NET Core 3.1),使用 BackgroundService
,当我想以编程方式停止服务时,我似乎遇到了问题。
我的主要功能如下
static async Task Main(string[] args)
{
IHost host = null;
try
{
host = CreateService(args).Build();
await host.RunAsync();
Console.WriteLine("Ending");
}
catch (Exception ex)
{
Console.WriteLine("Exception");
}
finally
{
if (host is IAsyncDisposable d) await d.DisposeAsync();
}
}
public static IHostBuilder CreateService(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "My Service";
})
.ConfigureServices((hostContext, services) =>
{
IConfiguration configuration = hostContext.Configuration;
ServiceOptions options = configuration.GetSection("ServiceOptions").Get<ServiceOptions>();
WorkerService sService = new WorkerService();
services.AddSingleton(options);
services.AddSingleton(sService);
services.AddHostedService<WindowsBackgroundService>(service => new WindowsBackgroundService(
service.GetService<WorkerService>(),
service.GetService<ILogger<WindowsBackgroundService>>(),
service.GetService<ServerOptions>()
));
});
后台服务如下:
public sealed class WindowsBackgroundService : BackgroundService
{
private WorkerService _workerService;
private ServerOptions _options;
private ILogger<WindowsBackgroundService> _logger;
public WindowsBackgroundService(
WorkerService workerService, ILogger<WindowsBackgroundService> logger,
ServiceOptions serviceOptions) => (_workerService, _logger, _options) = (workerService, logger, serviceOptions);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
bool allDone = false;
while (!stoppingToken.IsCancellationRequested && !allDone)
{
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
// Log to watchdog
// Check if we are in the run window
if (_options.InActivePeriod())
{
Console.WriteLine("Process Queue");
var processResult = _workerService.Process(_options);
if (!processResult && _workerService.FatalError)
{
_workerService = null;
allDone = true;
Console.WriteLine("Service Quitting");
}
}
}
Console.WriteLine($"Task Ending {stoppingToken.IsCancellationRequested}");
return;
}
}
}
所以一切 运行 都应该如此,我 运行 在调试器或命令行下进行此操作(我还没有实际将其安装为 Windows 服务,因为我还在写代码)。
函数 Process
正确执行。如果它遇到无法恢复的错误,它会将它设置为 FatalError
属性,这应该是一个信号,表明整个服务应该停止。任务确实完成了(正确的行被写入控制台)但是行 Console.WriteLine("Ending");
永远不会执行。看起来 host.RunAsync();
永远不会 returns(除非我按 CTRL+C)。
我现在不完全确定我做错了什么。谁能指出我的写作方向?
根据显示的代码,我没有看到任何会导致主机停止的东西。
托管服务,一旦启动与应用程序主机本身无关。所以即使ExecuteAsync
完成了,那个功能的完成也不代表宿主会停止运行.
您可以考虑将 IHostApplicationLifetime
注入托管服务并明确告诉应用程序以编程方式停止;
例如
public sealed class WindowsBackgroundService : BackgroundService {
private WorkerService _workerService;
private ServerOptions _options;
private ILogger<WindowsBackgroundService> _logger;
private IHostApplicationLifetime lifetime;
public WindowsBackgroundService(
WorkerService workerService, ILogger<WindowsBackgroundService> logger,
ServiceOptions serviceOptions, IHostApplicationLifetime lifetime)
=> (_workerService, _logger, _options, this.lifetime) = (workerService, logger, serviceOptions, lifetime);
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
bool allDone = false;
while (!stoppingToken.IsCancellationRequested && !allDone) {
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
// Log to watchdog
// Check if we are in the run window
if (_options.InActivePeriod()) {
Console.WriteLine("Process Queue");
var processResult = _workerService.Process(_options);
if (!processResult && _workerService.FatalError) {
_workerService = null;
allDone = true;
Console.WriteLine("Service Quitting");
lifetime.StopApplication(); //SIGNAL APPLICATION TO STOP
}
}
}
Console.WriteLine($"Task Ending {stoppingToken.IsCancellationRequested}");
return;
}
}
我也很好奇您是如何配置服务的。如果默认注册完成,您手动解决的所有问题都会自动注入,为什么还要使用工厂委托?
//...
.ConfigureServices((hostContext, services) => {
IConfiguration configuration = hostContext.Configuration;
ServiceOptions options = configuration.GetSection("ServiceOptions").Get<ServiceOptions>();
services.AddSingleton(options);
services.AddSingleton<WorkerService>();
services.AddHostedService<WindowsBackgroundService>();
});
Host.CreateDefaultBuilder