c# - 调度程序后台服务中的异步计时器
问题描述
我正在.Net-Core 中编写一个托管服务,它根据计时器在后台运行一项作业。
目前我必须像这样同步运行代码:
public override Task StartAsync(CancellationToken cancellationToken)
{
this._logger.LogInformation("Timed Background Service is starting.");
this._timer = new Timer(ExecuteTask, null, TimeSpan.Zero,
TimeSpan.FromSeconds(30));
return Task.CompletedTask;
}
private void ExecuteTask(object state)
{
this._logger.LogInformation("Timed Background Service is working.");
using (var scope = _serviceProvider.CreateScope())
{
var coinbaseService = scope.ServiceProvider.GetRequiredService<CoinbaseService>();
coinbaseService.FinalizeMeeting();
}
}
我想在计时器上运行此异步,但我不想使用火运行异步并忘记,因为它可能会导致我的代码中出现竞争条件。例如(订阅timer.Elapsed
事件)
有没有一种方法可以在定时调度上利用异步代码而无需执行触发并忘记
解决方案
对于那些正在寻找防止同时运行任务的完整示例的人。基于@Gabriel Luci 的回答和评论。
请随时发表评论,以便我更正。
/// <summary>
/// Based on Microsoft.Extensions.Hosting.BackgroundService https://github.com/aspnet/Extensions/blob/master/src/Hosting/Abstractions/src/BackgroundService.cs
/// Additional info: - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#timed-background-tasks
/// - https://stackoverflow.com/questions/53844586/async-timer-in-scheduler-background-service
/// </summary>
public abstract class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(ExecuteTask, null, TimeSpan.FromSeconds(30), TimeSpan.FromMilliseconds(-1));
return Task.CompletedTask;
}
private void ExecuteTask(object state)
{
_timer?.Change(Timeout.Infinite, 0);
_executingTask = ExecuteTaskAsync(_stoppingCts.Token);
}
private async Task ExecuteTaskAsync(CancellationToken stoppingToken)
{
await RunJobAsync(stoppingToken);
_timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task
/// </summary>
/// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
/// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
protected abstract Task RunJobAsync(CancellationToken stoppingToken);
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
public void Dispose()
{
_stoppingCts.Cancel();
_timer?.Dispose();
}
}
推荐阅读
- java - Commons DBUtils Oracle 11.2.0.4 与 Java 1.7 绑定参数 SQLException ORA-00942
- python - 使用 scikit-image 进行点跟踪未按预期工作
- android - 如何修复 AlertDialog 不包装到所有设备上的布局
- haskell - 在 Haskell 中创建滚动NDice 的函数的问题
- rust - 如何编写一个知道实现者是 [u8] 的 trait 方法?
- amazon-web-services - 无权执行:资源上的 sts:AssumeRole
- java - Java字符串/整数不会解析为枚举
- apache-spark - 推入现有本地表失败(Windows):InvalidRegionNumberException 然后 IllegalArgumentException
- c# - 似乎无法修复:System.BadImageFormatException:无法加载文件或程序集“System.Data.SqlClient”
- angular - NGRX 效果 - 分派的动作不按顺序执行