簡體   English   中英

為什么在 .NET Core 3.1 中停止 BackgroundService 時 IsCancellationRequested 未設置為 true?

[英]Why is IsCancellationRequested not set to true on stopping a BackgroundService in .NET Core 3.1?

我已經閱讀了大多數關於 .NET Core 3.1 中的IHostApplicationLifetime和 CancellationToken 的文章,但我找不到這不起作用的原因。

我有一個簡單的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 嗎? 這是一篇關於這個主題的好文章 那么為什么上面代碼片段中的 output 如下?

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 Stop. IsCancellationRequested: True

我希望能夠將此令牌傳遞給其他服務調用,以便它們能夠正常關閉。

這里有很多不同的取消標記,以及幾個不同的抽象( IHostApplicationLifetimeIHostedServiceBackgroundService )。 解開一切需要一段時間。 您鏈接到的博客文章很棒,但 go 並沒有詳細介紹CancellationToken

首先,如果您要使用BackgroundService ,我建議您閱讀代碼 另外,我強烈建議不要覆蓋StartAsyncStopAsync BackgroundService以一種非常特殊的方式使用它們。

IHostedService有兩種方法。 StartAsync啟動服務運行(可能異步); 它需要一個CancellationToken來指示應該取消“開始”操作(我沒有檢查過,但我認為只有當應用程序幾乎立即關閉時才會觸發此令牌)。 請注意,在“已啟動”或“正在運行” StartAsync中考慮托管服務之前,需要完成StartAsync。 同樣, StopAsync停止服務(可能是異步的)。 當應用程序開始正常關閉時調用StopAsync 正常關閉期間有一個超時,之后應用程序開始“我現在是認真的”關閉。 StopAsyncCancellationToken表示從“優雅”到“我現在很認真”的過渡。 所以在正常關機超時 window 期間沒有設置它。

如果您直接使用BackgroundService而不是IHostedService (像大多數人一樣),您會在ExecuteAsync中獲得不同CancellationToken 這是在調用BackgroundService.StopAsync設置的 - 即,當應用程序開始正常關閉時。 所以它大致相當於IHostApplicationLifetime.ApplicationStopping ,但僅限於單個托管服務。 您可以期望在設置IHostApplicationLifetime.ApplicationStopping后不久設置BackgroundWorker.ExecuteAsync CancellationToken

請注意,所有這些CancellationToken都代表不同的東西:

  • IHostedService.StartAsyncCancellationToken表示“中止此服務的啟動”。
  • IHostedService.StopAsyncCancellationToken表示“立即停止此服務;您超出寬限期”。
  • IHostApplicationLifetime.ApplicationStopping的意思是“整個應用程序的正常關閉序列已經開始;每個人都請停止你正在做的事情”。
    • 作為正常關閉序列的一部分,將調用所有IHostedService.StopAsync方法。
  • BackgroundService.ExecuteAsyncCancellationToken表示“停止此服務”。

有趣的是, BackgroundService類型通常看不到“我現在很認真”的信號。 他們只看到“停止此服務”的信號。 這可能是因為CancellationToken所代表的“我現在是認真的”信號有些令人困惑。

如果您查看Host的代碼,則關閉序列在其關閉序列中使用了更多取消標記:

  1. IHost.StopAsync采用CancellationToken 表示“停止不應再優雅”
  2. 然后它在正常超時期間啟動基於CancellationToken的超時
  3. ...以及另一個鏈接的CancellationToken ,如果IHost.StopAsync令牌被觸發或計時器已過, IHost.StopAsync觸發。 所以這個也意味着“停止不應該再優雅”。
  4. 接下來它調用IHostApplicationLifetime.StopApplication ,它取消了IHostApplicationLifetime.ApplicationStopping CancellationToken
  5. 然后它為每個IHostedService調用StopAsync ,傳遞“停止應該不再是優雅的”令牌。
    • 所有BackgroundService類型都有自己的CancellationToken (在啟動期間傳遞給ExecuteAsync ),並且這些取消令牌StopAsync取消
  6. 最后,它調用IHostApplicationLifetime.NotifyStopped ,它取消了IHostApplicationLifetime.ApplicationStopped CancellationToken

我為“不再優雅”信號計數 3(一個傳入,一個計時器,一個鏈接這兩個),加上IHostApplicationLifetime上的 2 個,每個BackgroundService加上 1 個,在關機期間總共使用了5 + n取消令牌。 :)

傳遞給StopAsyncCancellationToken指示BackgroundService是否必須執行正常關閉或硬關閉。

您使用kill -s TERM停止該進程,以便它發送SIGTERM信號要求應用程序正常關閉。 因此IsCancellationRequested屬性仍為 false。

要將令牌傳遞給其他服務調用,您必須提供自己的CancellationToken 您可以使用CancellationTokenSource來管理令牌的創建和取消。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM