如何 运行 .net core 后台服务中的多个任务具有不同的定时器持续时间

How to run multiple task in background service in .net core with different timer duration

我正在创建一个辅助服务,它将 运行 作为 windows 服务。我有一个要求,我想调用两个可能有不同计时器的任务。

DoWork 应该每 5 分钟调用一次,DoAnotherWork 应该每 10 分钟左右调用一次。这两个任务可以 运行 并行并且互不依赖。

我能够创建任务 DoWork,每 5 分钟可以 运行。我对如何实现另一个具有不同计时器持续时间的任务感到困惑?

public class Worker : BackgroundService
{        
    private readonly IServiceScopeFactory _scopeFactory;        
    private IDataLoaderService _dataLoaderService;        

    public override Task StartAsync(CancellationToken cancellationToken)
    {
        using var scope = _scopeFactory.CreateScope();
        _dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();                        
        return base.StartAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {            
        while (!stoppingToken.IsCancellationRequested)
        {
            await DoWork(stoppingToken, _dataLoaderService);
            await Task.Delay(300000, stoppingToken); //Run every 5 minutes

            await DoAnotherWork(stoppingToken, _dataLoaderService);
            await Task.Delay(600000, stoppingToken); //Run every 10 minutes
        }
    }

    private async Task DoWork(CancellationToken stoppingToken, IDataLoaderService loaderService)
    {
        await loaderService.Process();            
    }        
    
    private async Task DoAnotherWork(CancellationToken stoppingToken, IDataLoaderService loaderService)
    {
        await loaderService.Validate();            
    }
}

如果您不想在您的案例中使用现有的调度库,您可以使用两个计时器,例如 docs,其中使用了 System.Threading.Timer。类似的东西:

public class Worker : IHostedService, IDisposable
{        
    private readonly IServiceScopeFactory _scopeFactory;        
    private IDataLoaderService _dataLoaderService;  
    private Timer _timer1;
    private Timer _timer2;      

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();              
        _timer1 = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(300));
        _timer2 = new Timer(DoAnotherWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(600));
        return Task.CompletedTask;
    }    
   
    private async void DoWork(object _)
    {
        // or create scope and resolve here
        await _loaderService.Process();            
    }        
    
    private async void DoAnotherWork(object _)
    {
        // or create scope and resolve here
        await _loaderService.Validate();            
    }

    public Task StopAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Timed Hosted Service is stopping.");

        _timer1?.Change(Timeout.Infinite, 0);
        _timer2?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer1?.Dispose();
        _timer2?.Dispose();
    }
}

These two tasks can run in parallel and are not dependant on each other.

在我看来你有两项服务:

public class ProcessDataLoaderWorker : BackgroundService
{
  private readonly IServiceScopeFactory _scopeFactory;

  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    using var scope = _scopeFactory.CreateScope();
    var dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();

    while (true)
    {
      await dataLoaderService.Process();
      await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); //Run every 5 minutes
    }
  }
}

public class ValidateDataLoaderWorker : BackgroundService
{
  private readonly IServiceScopeFactory _scopeFactory;

  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    using var scope = _scopeFactory.CreateScope();
    var dataLoaderService = scope.ServiceProvider.GetRequiredService<IDataLoaderService>();

    while (true)
    {
      await dataLoaderService.Validate();
      await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken); //Run every 10 minutes
    }
  }
}

我还修改了 IDataLoaderService 的使用方式,使其不在其范围之外使用,并更改了 Task.Delay 参数,使其更易于解释。