Некоторое время назад у меня был самостоятельно созданный планировщик, который был создан с помощью '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 секунд. Но, если я прав, предыдущее задание будет продолжено в это время! Это означает, что у меня может быть беспорядок в моей БД в результате.