1. 程式人生 > >ASP.NET Core 2.2 基礎知識(九) 使用托管服務實現後臺任務

ASP.NET Core 2.2 基礎知識(九) 使用托管服務實現後臺任務

修改 start code 創建 我們 pin iis logger 觸發

原文:ASP.NET Core 2.2 基礎知識(九) 使用托管服務實現後臺任務

在 ASP.NET Core 中,後臺任務作為托管服務實現.托管服務是一個類,而且必須實現 IHostedService 接口,該接口定義了兩個方法:

  • StartAsync(CancellationToken cancellationToken) 該方法包含啟動後臺任務的邏輯,當啟動服務器並觸發 IApplicationLifetime.ApplicationStarted 後調用該方法.
  • StopAsync(CancellationToken cancellationToken)主機正常關閉時觸發,包含結束後臺任務和處理任何非托管資源的邏輯.如果應用意外關閉,則可能不會調用.

托管服務在應用啟動時激活一次,在應用關閉時正常關閉.實現 IDisposable 時,可在處置服務容器時處理資源.如果在執行後臺任務期間引發錯誤,即使未調用 StopAsync ,也應調用 Dispose.

示例一:計時的後臺任務

    public class TimedHostedService : IHostedService, IDisposable
    {

        private readonly ILogger _logger;
        private Timer _timer;
        public TimedHostedService(ILogger<TimedHostedService> logger)
        {
            _logger 
= logger; } public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Timed Background Service is starting." + DateTime.Now); _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));//立即執行一次,每5秒執行一次 return
Task.CompletedTask; } private void DoWork(object state) { _logger.LogInformation("Timed Background Service is working." + DateTime.Now); } public Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("Timed Background Service is stopping." + DateTime.Now); _timer?.Change(Timeout.Infinite, 0);//不再執行 return Task.CompletedTask; } public void Dispose() { _timer?.Dispose(); } }

註冊該後臺任務:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddHostedService<TimedHostedService>();
        }

在控制臺啟動該項目,期間用 ctrl+C 結束應用.

技術分享圖片

其實官方幫我們封裝了一個類來簡化上述代碼:

  /// <summary>
  /// Base class for implementing a long running <see cref="T:Microsoft.Extensions.Hosting.IHostedService" />.
  /// </summary>
  public abstract class BackgroundService : IHostedService, IDisposable

因此上述代碼可以修改成:

    public class MyBackGroundTask : BackgroundService
    {
        private readonly ILogger _logger;

        private Timer _timer;

        public MyBackGroundTask(ILogger<MyBackGroundTask> logger)
        {
            _logger = logger;
        }
        
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation($" MyBackGroundTask is starting. {DateTime.Now}");
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation($" MyBackGroundTask is working. {DateTime.Now}");
                await Task.Delay(5000, stoppingToken);
            }
            _logger.LogInformation($" MyBackGroundTask is stopping. {DateTime.Now}");
        }
    }

但是,我發現

_logger.LogInformation($" MyBackGroundTask is stopping. {DateTime.Now}");

這句代碼始終不執行.不知道是哪裏沒搞對.希望大神能幫個忙..

示例二:在後臺任務中使用有作用域的服務

要使用有作用域的服務,需要先創建一個作用域.默認情況下,不會為托管服務創建作用域.

    public interface IScopedProcessingService
    {
        void DoWork();
    }

    public class ScopedProcessingService : IScopedProcessingService
    {
        private readonly ILogger _logger;
        public ScopedProcessingService(ILogger<ScopedProcessingService> logger)
        {
            _logger = logger;
        }
        public void DoWork()
        {
            _logger.LogInformation($"Scoped Processing Service is working. {DateTime.Now}");
        }
    }

    public class ConsumeScopedServiceHostedService : IHostedService
    {

        private readonly ILogger _logger;

        public IServiceProvider Services { get; }

        public ConsumeScopedServiceHostedService(IServiceProvider services, ILogger<ConsumeScopedServiceHostedService> logger)
        {
            Services = services;
            _logger = logger;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation($"Consume Scoped Service Hosted Service is starting. {DateTime.Now}");
            DoWork();
            return Task.CompletedTask;
        }

        private void DoWork()
        {
            _logger.LogInformation($"Consume Scoped Service Hosted Service is working. {DateTime.Now}");
            using (IServiceScope scope = Services.CreateScope())//創建一個作用域.
            {
                IScopedProcessingService scopedProcessingService = scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
                scopedProcessingService.DoWork();
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation($"Consume Scoped Service Hosted Service is stopping. {DateTime.Now}");
            return Task.CompletedTask;
        }
    }

但是我真的沒搞懂官方這個例子的作用.因為托管服務只會激活一次,有作用域又有什麽價值呢?希望哪位大哥能解答一下.

下面的在摘自網絡:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_11.html

經測試:

  1. 當IIS上部署的項目啟動後,後臺任務隨之啟動,任務執行相應的log正常輸出。

  2. 手動回收對應的應用程序池,任務執行相應的log輸出停止。

  3. 重新請求該網站,後臺任務隨之啟動,任務執行相應的log重新開始輸出。

所以不建議在這樣的後臺任務中做一些需要固定定時執行的業務處理類的操作,但對於緩存刷新類的操作還是可以的,因為當應用程序池回收後再次運行的時候,後臺任務會隨著啟動。

ASP.NET Core 2.2 基礎知識(九) 使用托管服務實現後臺任務