0

前段时间,我在“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 服务。这是微软文档。我会告诉你结果:

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>();

那么如何保护我的应用程序免受并行作业的影响?我需要一个一个。在上面的示例中,下一个任务将在 10 秒后开始。但是,如果我猜对了,之前的任务会在这个时候继续进行!这意味着我的数据库可能因此而混乱。

4

1 回答 1

1

这是对 async-await 工作原理的误解。

每个任务将按顺序调用,因为正在等待任务。

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);
    }
}

不应有重叠,因为任务将一个接一个地等待。

于 2019-10-17T21:55:55.023 回答