Можно ли запускать мои задания (основанные на Scoped Background Services) по очереди? - PullRequest
0 голосов
/ 18 октября 2019

Некоторое время назад у меня был самостоятельно созданный планировщик, который был создан с помощью 'System.Timers'. Я покажу вам этот код:

public class Scheduler
{
    private const int MSecond = 1000;
    private readonly int _seconds = MSecond * 10;
    private Timer _aTimer;

    public void Start()
    {
        Console.WriteLine("Sending is started ...");

        _aTimer = new Timer();
        _aTimer.Interval = _seconds;

        _aTimer.Elapsed += OnTimedEvent;

        _aTimer.AutoReset = true;

        _aTimer.Enabled = true;
    }

    public bool IsWorking()
    {
        return _aTimer != null;
    }

    private async void OnTimedEvent(object sender, ElapsedEventArgs e)
    {
        await JustDoIt();
    }

    private async Task JustDoIt()
    {
        _aTimer.Stop();

        // big and difficult work
        await Task.Delay(1000 * 12);
        Console.WriteLine("Done !!");

        _aTimer.Start();
    }

    public void Stop()
    {
        _aTimer.Stop();
        _aTimer = null;
    }
}

Итак, чтобы быть уверенным, что я выполнил одно задание, прежде чем начинать новое, я просто отменяю таймер сразу после запуска задания. Затем, когда работа закончена, я включаю свой таймер. Это помогает мне избежать ошибок, когда несколько параллельных заданий пытаются получить доступ к ресурсам или записывают в базу данных вместе. И мои бизнес-правила прямо говорят мне: рабочие места должны идти один за другим, а не вместе.

Все было в порядке, но я решил переписать все в службах Scoped Hosted Background. Вот документация Microsoft . Я покажу вам результат:

IScopedProcessingService

internal interface IScopedProcessingService
{
    Task DoWork(CancellationToken stoppingToken);
}

ConsumeScopedServiceHostedService

public class ConsumeScopedServiceHostedService : BackgroundService
{
    private readonly ILogger<ConsumeScopedServiceHostedService> _logger;

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

    public IServiceProvider Services { get; }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        _logger.LogInformation("Consume Scoped Service Hosted Service is working.");

        while (!stoppingToken.IsCancellationRequested) {
            using (var scope = Services.CreateScope()) {
                IServiceProvider serviceProvider = scope.ServiceProvider;
                var service = serviceProvider.GetRequiredService<IScopedProcessingService>();    
                await service.DoWork(stoppingToken);
            }
            //Add a delay between executions.
            await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
        }
    }
    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation(
            "Consume Scoped Service Hosted Service is stopping.");

        await Task.CompletedTask;
    }
}

и ScopedProcessingService

internal class ScopedProcessingService : IScopedProcessingService
{
   // here is injections and constructor

    public async Task DoWork(CancellationToken stoppingToken)
    {
      // a job with no fixed time. Sometimes it's minute, sometimes it's more, sometimes it's less           
        await Task.Delay(TimeSpan.FromSeconds(40));

    }
}

А в Startup.cs

        services.AddHostedService<ConsumeScopedServiceHostedService>();
        services.AddScoped<IScopedProcessingService, ScopedProcessingService>();

Итак, как я могу защитить свое приложение от заданий Parallels? Мне нужен ровно один за другим. В приведенном выше примере следующая задача начнется через 10 секунд. Но, если я прав, предыдущее задание будет продолжено в это время! Это означает, что у меня может быть беспорядок в моей БД в результате.

1 Ответ

1 голос
/ 18 октября 2019

Это неправильное понимание того, как работает асинхронное ожидание.

Каждая задача будет вызываться последовательно, потому что задачи ожидаются.

protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
    _logger.LogInformation("Consume Scoped Service Hosted Service is working.");

    while (!stoppingToken.IsCancellationRequested) {
        using (var scope = Services.CreateScope()) {
            IServiceProvider serviceProvider = scope.ServiceProvider;
            var service = serviceProvider.GetRequiredService<IScopedProcessingService>();    
            await service.DoWork(stoppingToken);
        }
        //Add a delay between executions.
        await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
    }
}

Не должно быть совпадений, поскольку задачи будут ожидаться одна за другой.

...