BackgroundService and IHostedService

IHostedService:

适用于需要更高灵活性和控制的场景。
需要自定义启动和停止逻辑。
适用于复杂的后台任务管理。
BackgroundService:

适用于需要简单实现后台任务的场景。
提供了一个方便的抽象,减少样板代码。
适用于大多数常见的后台任务。

public class MyHostedService : IHostedService
{
    private readonly ILogger<MyHostedService> _logger;
    private Timer _timer;

    public MyHostedService(ILogger<MyHostedService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("MyHostedService is starting.");
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("MyHostedService is working.");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("MyHostedService is stopping.");
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

在 BackgroundService 中,StopAsync 是用来优雅地停止服务的。当应用程序停止时,StopAsync 会被调用,并且 CancellationToken 会被触发,这将会请求 ExecuteAsync 停止运行。然而,StopAsync 并不会等待 ExecuteAsync 自行完成;相反,StopAsync 会立即开始执行。

为了确保 ExecuteAsync 完成后再执行 StopAsync 中的逻辑,可以在 ExecuteAsync 内部检测到取消请求时进行清理,然后将 ExecuteAsync 任务与 StopAsync 协调起来。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<MyBackgroundService>();
            })
            .Build();

        await host.RunAsync();
    }
}

public class MyBackgroundService : BackgroundService
{
    private readonly ILogger<MyBackgroundService> _logger;
    private Task _executingTask;
    private readonly IHostApplicationLifetime _applicationLifetime;
    private CancellationTokenSource _stoppingCts = new CancellationTokenSource();

    public MyBackgroundService(ILogger<MyBackgroundService> logger, IHostApplicationLifetime applicationLifetime)
    {
        _logger = logger;
        _applicationLifetime = applicationLifetime;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("MyBackgroundService is starting.");

        stoppingToken.Register(() => _logger.LogInformation("MyBackgroundService is stopping."));

        _executingTask = Task.Run(async () =>
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("MyBackgroundService is running.");
                await Task.Delay(5000, stoppingToken);
            }

            _logger.LogInformation("MyBackgroundService is completing background work.");
            // 在这里添加任何清理逻辑,例如关闭资源等

            _logger.LogInformation("MyBackgroundService has stopped.");
        }, stoppingToken);

        return _executingTask;
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("MyBackgroundService is executing StopAsync.");

        // 请求停止
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            _stoppingCts.Cancel();
        }
        finally
        {
            // 等待任务完成
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }
        
        _logger.LogInformation("MyBackgroundService has completed StopAsync.");
    }
    
    public override void Dispose()
    {
        _stoppingCts.Cancel();
        base.Dispose();
    }

    public async Task StopServiceAsync()
    {
        _logger.LogInformation("Stopping the service internally...");
        _applicationLifetime.StopApplication();
    }
}

参考:
BackgroundService Graceful Shutdown - Complete work and write to DB
https://stackoverflow.com/questions/70036809/backgroundservice-graceful-shutdown-complete-work-and-write-to-db

posted @   Josen_Earth  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示