Почему для IsCancellationRequested не установлено значение true при остановке BackgroundService в. NET Core 3.1? - PullRequest
0 голосов
/ 08 апреля 2020

Я прочитал большинство статей, которые я могу найти о IHostApplicationLifetime и CancellationToken's in. NET Core 3.1, но я не могу найти причину, почему это не работает.

У меня есть простой BackgroundService , который выглядит следующим образом:

    public class AnotherWorker : BackgroundService
    {
        private readonly IHostApplicationLifetime _hostApplicationLifetime;

        public AnotherWorker(IHostApplicationLifetime hostApplicationLifetime)
        {
            _hostApplicationLifetime = hostApplicationLifetime;
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            Console.WriteLine($"Process id: {Process.GetCurrentProcess().Id}");
            _hostApplicationLifetime.ApplicationStarted.Register(() => Console.WriteLine("Started"));
            _hostApplicationLifetime.ApplicationStopping.Register(() => Console.WriteLine("Stopping"));
            _hostApplicationLifetime.ApplicationStopped.Register(() => Console.WriteLine("Stopped"));

            return Task.CompletedTask;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            Console.WriteLine("Executing");
            return Task.CompletedTask;
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {
        // This actually prints "Stop. IsCancellationRequested: False". Why?
            Console.WriteLine($"Stop. IsCancellationRequested: {cancellationToken.IsCancellationRequested}");
            await base.StopAsync(cancellationToken);
        }
    }

ConsoleLifetime добавляется по умолчанию, который прослушивает Ctrl + C и SIGTERM и сообщает IHostApplicationLifetime. Я думаю, что IHostApplicationLifetime в свою очередь должен затем отменить все токены CancellationTokens? Вот хорошая статья на эту тему . Итак, почему вывод из приведенного выше фрагмента кода следующий?

Hosting starting
Started
Hosting started
(sends SIGTERM with `kill -s TERM <process_id>`)
Applicationis shuting down...
Stop. IsCancellationRequested: False
Stopped
Hosting stopped

Я бы ожидал, что он будет записывать Stop. IsCancellationRequested: True

Я хочу иметь возможность передать этот токен для других сервисных вызовов, чтобы они могли корректно завершить работу.

Ответы [ 2 ]

2 голосов
/ 09 апреля 2020

Здесь много разных токенов отмены и несколько разных абстракций (IHostApplicationLifetime, IHostedService, BackgroundService). Требуется время, чтобы распутать все. Сообщение в блоге, на которое вы ссылаетесь, является fantasti c, но не go в деталях на CancellationToken s.

Во-первых, если вы собираетесь использовать BackgroundService, я рекомендую чтение кода . Также настоятельно рекомендую не переопределять StartAsync и StopAsync; BackgroundService использует их очень особым образом.

IHostedService имеет два метода. StartAsync запускает службу (возможно асинхронно); для этого требуется CancellationToken, означающее, что операция «запуск» должна быть отменена (я не проверял, но я предполагаю, что этот токен срабатывает только в том случае, если приложение закрывается почти сразу). Обратите внимание, что StartAsync необходимо завершить , прежде чем размещенная служба будет переведена в состояние «запущено» или «запущено». Точно так же StopAsync останавливает службу (возможно, асинхронно). StopAsync вызывается, когда приложение начинает свое постепенное завершение. Есть тайм-аут на период изящного завершения работы, после которого приложение начинает отключение «Я серьезно сейчас». CancellationToken для StopAsync представляет переход от «изящного» к «я серьезно сейчас». Таким образом, не устанавливается в течение этого времени изящного отключения.

Если вы используете BackgroundService вместо IHostedService напрямую (как большинство людей делают), вы получаете другой CancellationToken в ExecuteAsync. Этот устанавливается , когда вызывается BackgroundService.StopAsync, т.е. когда приложение начало постепенного завершения работы. Таким образом, он примерно эквивалентен IHostApplicationLifetime.ApplicationStopping, но ограничен одной размещенной службой. Можно ожидать, что BackgroundWorker.ExecuteAsync CancellationToken будет установлен вскоре после установки IHostApplicationLifetime.ApplicationStopping.

Обратите внимание, что все эти CancellationToken s представляют что-то другое:

  • IHostedService.StartAsync s CancellationToken означает «прервать запуск этой службы».
  • IHostedService.StopAsync s CancellationToken означает «остановить эту службу прямо сейчас ; вы вышли из льготный период ".
  • IHostApplicationLifetime.ApplicationStopping означает" изящная последовательность завершения работы всего этого приложения; всем, пожалуйста, прекратите то, что вы делаете ".
    • Как часть последовательности изящного отключения, все методы IHostedService.StopAsync вызываются.
  • BackgroundService.ExecuteAsync 's CancellationToken означает «остановить эту службу».

Интересно отметить, что BackgroundService типы обычно не видят сигнал "Я серьезно сейчас"; они видят только сигнал «остановить эту услугу». Вероятно, это связано с тем, что сигнал «Я серьезен сейчас», представленный CancellationToken, несколько сбивает с толку.

Если вы посмотрите на код для Host, последовательность выключения будет еще больше токены отмены, используемые в последовательности выключения:

  1. IHost.StopAsync принимает CancellationToken , что означает "остановка больше не должна быть изящной" .
  2. Затем начинает CancellationToken тайм-аут на льготный период .
  3. ... и другой связанный CancellationToken, который срабатывает, если либо токен IHost.StopAsync, либо если таймер истек. Таким образом, этот также означает, что «остановка больше не должна быть изящной».
  4. Далее вызывает IHostApplicationLifetime.StopApplication, что отменяет IHostApplicationLifetime.ApplicationStopping CancellationToken.
  5. Затем вызывает StopAsync для каждого IHostedService, передавая токен "Стоп больше не должен быть изящным".
    • Все типы BackgroundService имеют свои CancellationToken (которые были переданы ExecuteAsync во время запуска), и эти токены отмены отменены StopAsync.
  6. Наконец, вызывает IHostApplicationLifetime.NotifyStopped, что отменяет IHostApplicationLifetime.ApplicationStopped CancellationToken.

Я считаю 3 для сигнала «больше не изящный» (один переданный, один таймер и один связывающий эти два), плюс 2 на IHostApplicationLifetime, плюс 1 для каждого BackgroundService, всего 5 + n токенов отмены, используемых во время выключения , :)

0 голосов
/ 08 апреля 2020

Значение CancellationToken, переданное StopAsync, указывает, должно ли BackgroundService выполнить корректное или жесткое завершение.

Вы останавливаете процесс, используя kill -s TERM, поэтому он отправляет SIGTERM сигнал с просьбой о корректном завершении работы приложения. Поэтому свойство IsCancellationRequested все еще имеет значение false.

Чтобы передать токен на вызовы других служб, вы должны предоставить свой собственный CancellationToken. Вы можете использовать CancellationTokenSource для управления созданием и отменой токена.

...