简体   繁体   中英

How do I (gracefully) shut down a worker service from within itself?

I'm using the .NET Core 3.1 worker service template to build a Windows Service.

I gather that the basic flow should be handled within ExecuteAsync , roughly like so:

        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);
            }
        }

I'm trying to properly signal from within the process that I want the app to shut down.

When stopping the service, I assume StopAsync() gets called. This seems to work fine. So I thought await StopAsync() would do the trick, but afterwards, ExecuteAsync still keeps running (so I'm guessing StopAsync won't request cancellation, and you're not supposed to call it yourself).

So I amended my loop with another bool:

            while (!stoppingToken.IsCancellationRequested && !_ShuttingDown)

This does exit the loop, and never enters ExecuteAsync again. However, the app just keeps on running. According to the debugger, it just stays on host.Run() .

How do I signal to the application host that I want to shut down?

In ASP.NET Core, background services are independent from the application. It's possible, eg, for services to finish and yet the application continues executing. If you want your application to exit when your background service finishes, then you'll need to wire that up yourself.

You can inject IHostApplicationLifetime into your service and then call IHostApplicationLifetime.StopApplication . Something like this:

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();
    }
  }
}

Services are not supposed to stop themselves, they are not applications - they are services...

Why do you want to stop the service from within the service? Perhaps you want a scheduled task instead of a service.

You could always use a ServiceController or similar class to send the stop event to the service

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM