任务完成后 IHost 未返回

Posted

技术标签:

【中文标题】任务完成后 IHost 未返回【英文标题】:IHost not returning when Task completed 【发布时间】:2022-01-15 07:15:44 【问题描述】:

我正在编写一个 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(); 永远不会返回(除非我执行 CTRL+C)。

目前我不完全确定我做错了什么。谁能指出我的写作方向?

【问题讨论】:

根据显示的代码,我看不到任何会导致主机停止的情况。 我很好奇您是如何配置服务的。如果默认注册完成,您手动解决的所有内容都会自动注入,为什么还要使用工厂委托。? 【参考方案1】:

根据显示的代码,我看不到任何会导致主机停止的情况。

托管服务一旦启动,就与应用程序主机本身无关。所以即使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 会为您添加日志记录和主机生命周期

【讨论】:

谢谢,IHostApplicationLifetime 的注入对我有用。 就服务配置的原因而言,简单的答案是我试图对问题进行排序并尝试了一些不同的方法。接下来我会回去重新检查。

以上是关于任务完成后 IHost 未返回的主要内容,如果未能解决你的问题,请参考以下文章

Elixir 长时间运行的后台任务未完成,有时会崩溃

channel关闭后立即返回false吗

前端监听服务端状态并返回状态怎么实现

promise

在任务仍在运行时返回结果

如何让任务在 Swift 3 中等待完成