是否可以 运行 我的工作(基于范围后台服务)一项一项?
Is it possible to run my jobs (that are based on Scoped Background Services) one by one?
前段时间我在 '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;
}
}
所以,为了确保在开始新工作之前我已经完成了一项工作,我只是在开始工作后直接取消了我的计时器。然后,当工作完成后,我打开我的计时器。当多个并行作业试图访问资源或一起写入数据库时,这有助于我避免错误。而我的业务规则直接告诉我:jobs mast 一个接一个,而不是一起走。
一切都很好,但我决定在 Scoped Hosted Background 服务上重写所有内容。这里是Microsoft documentation。我会告诉你结果:
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 秒后开始。但是,如果我做对了,之前的任务会在这次继续进行!这意味着我的数据库可能会因此变得一团糟。
这是对 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);
}
}
应该没有重叠,因为任务将一个接一个地等待。
前段时间我在 '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;
}
}
所以,为了确保在开始新工作之前我已经完成了一项工作,我只是在开始工作后直接取消了我的计时器。然后,当工作完成后,我打开我的计时器。当多个并行作业试图访问资源或一起写入数据库时,这有助于我避免错误。而我的业务规则直接告诉我:jobs mast 一个接一个,而不是一起走。
一切都很好,但我决定在 Scoped Hosted Background 服务上重写所有内容。这里是Microsoft documentation。我会告诉你结果:
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 秒后开始。但是,如果我做对了,之前的任务会在这次继续进行!这意味着我的数据库可能会因此变得一团糟。
这是对 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);
}
}
应该没有重叠,因为任务将一个接一个地等待。