【问题标题】:How do I (gracefully) shut down a worker service from within itself?我如何(优雅地)从内部关闭工作人员服务?
【发布时间】:2019-12-27 10:03:30
【问题描述】:

我正在使用 .NET Core 3.1 辅助服务模板来构建 Windows 服务。

我认为基本流程应该在ExecuteAsync 内处理,大致如下:

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    _Logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                    await SomeMethodThatDoesTheWork(stoppingToken);
                }
                catch (Exception ex)
                {
                    _Logger.LogError(ex, "Global exception occurred. Will resume in a moment.");
                }

                await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
            }
        }

我正在尝试从进程中正确发出我希望应用关闭的信号。

当停止服务时,我假设StopAsync() 被调用。这似乎工作正常。所以我认为await StopAsync() 可以解决问题,但之后ExecuteAsync 仍然会继续运行(所以我猜StopAsync 不会请求取消,而且你不应该自己调用它)。

所以我用另一个 bool 修改了我的循环:

            while (!stoppingToken.IsCancellationRequested && !_ShuttingDown)

这确实退出了循环,并且再也不会进入ExecuteAsync。但是,该应用程序只是继续运行。根据调试器,它只是停留在host.Run()

如何向应用程序主机发出我要关闭的信号?

【问题讨论】:

标签: c# asp.net .net-core


【解决方案1】:

在 ASP.NET Core 中,后台服务独立于应用程序。例如,有可能服务完成但应用程序继续执行。如果您希望您的应用程序在后台服务完成后退出,那么您需要自己连接。

您可以inject IHostApplicationLifetime into your service and then call IHostApplicationLifetime.StopApplication。像这样的:

public sealed class MyService : BackgroundService
{
  private readonly IHostApplicationLifetime _hostApplicationLifetime;
  private readonly ILogger<MyService> _logger;

  public MyService(IHostApplicationLifetime hostApplicationLifetime, ILogger<MyService> logger)
  {
    _hostApplicationLifetime = hostApplicationLifetime;
    _logger = logger;
  }

  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    try
    {
      while (!stoppingToken.IsCancellationRequested)
      {
        try
        {
          _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
          await SomeMethodThatDoesTheWork(stoppingToken);
        }
        catch (Exception ex)
        {
          _logger.LogError(ex, "Global exception occurred. Will resume in a moment.");
        }

        await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
      }
    }
    finally
    {
      _logger.LogCritical("Exiting application...");
      _hostApplicationLifetime.StopApplication();
    }
  }
}

【讨论】:

  • "您可以将 IHostApplicationLifetime 注入您的服务,然后调用 IHostApplicationLifetime.StopApplication。"宾果游戏,我想这就是我要找的东西!
  • 调用 StopAsync() 也可以吗?
  • @JDawg:你的意思是IHost?我相信这将等待所有后台服务完成,包括这个。
【解决方案2】:

服务不应该自行停止,它们不是应用程序 - 它们是服务...

为什么要从服务中停止服务?也许您需要计划任务而不是服务。

您总是可以使用 ServiceController 或类似的类将停止事件发送到服务

【讨论】:

  • 这里的原始用例是用于自我更新。该服务收到一个新版本可用的信号,并且现有版本应该正常关闭。不过,我还可以设想 API 会请求关闭的其他用例。
  • 您可能应该使用辅助“助手”或“引导”服务来执行此操作。想象一下,你将一个拙劣的更新推送到服务,然后它就不能再更新自己了
  • 对,但我仍然希望让主进程优雅地关闭……肯定有比停止服务更好/更少迂回的方法吗?
  • 这个“答案”不应该是评论而不是答案吗?
猜你喜欢
  • 1970-01-01
  • 2015-07-04
  • 2011-12-12
  • 1970-01-01
  • 2010-09-06
  • 1970-01-01
  • 1970-01-01
  • 2019-07-25
  • 1970-01-01
相关资源
最近更新 更多