Microsoft在Implement background tasks in microservices with IHostedService and the BackgroundService class处永久/连续IHostedService
的示例使用while
+ Task.Delay
'模式'。
这用一个简化的代码片段在下面说明。
public class GracePeriodManagerService : BackgroundService
(...)
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//Do work
await Task.Delay(timeSpan, stoppingToken);
}
}
此模式的变化缓慢-每timeSpan
+ how_long_work_took
都会完成一次工作。即使how_long_work_took
在一段时间内非常小,它也会加起来。
我想避免根据timeSpan
花费的时间来计算work
。
什么是在每个 fixed_amount_of_time 时间都运行的健壮解决方案?。
大声思考:如果我使用任务调度程序库,例如HangFire,则在ExecuteAsync
内使用IHostedService
/ BackgroundService
确实有意义吗?
奖励是能够在某个时间点(例如午夜)执行任务
答案 0 :(得分:1)
这就是我处理此类事情的方式...就我而言,我需要在特定的日期,特定的时间启动服务,并每隔x天重复一次。但是我不知道这到底是不是你在寻找什么:)
public class ScheduleHostedService: BackgroundService
{
private readonly ILogger<ScheduleHostedService> _logger;
private readonly DaemonSettings _settings;
public ScheduleHostedService(IOptions<DaemonSettings> settings, ILogger<ScheduleHostedService> logger)
{
_logger = logger;
_settings = settings.Value;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
DateTime? callTime=null;
if (_settings.StartAt.HasValue)
{
DateTime next = DateTime.Today;
next = next.AddHours(_settings.StartAt.Value.Hour)
.AddMinutes(_settings.StartAt.Value.Minute)
.AddSeconds(_settings.StartAt.Value.Second);
if (next < DateTime.Now)
{
next = next.AddDays(1);
}
callTime = next;
}
if (_settings.StartDay.HasValue)
{
callTime = callTime ?? DateTime.Now;
callTime = callTime.Value.AddDays(-callTime.Value.Day).AddDays(_settings.StartDay.Value);
if (callTime < DateTime.Now)
callTime = callTime.Value.AddMonths(1);
}
if(callTime.HasValue)
await Delay(callTime.Value - DateTime.Now, stoppingToken);
else
{
callTime = DateTime.Now;
}
while (!stoppingToken.IsCancellationRequested)
{
//do smth
var nextRun = callTime.Value.Add(_settings.RepeatEvery) - DateTime.Now;
await Delay(nextRun, stoppingToken);
}
}
static async Task Delay(TimeSpan wait, CancellationToken cancellationToken)
{
var maxDelay = TimeSpan.FromMilliseconds(int.MaxValue);
while (wait > TimeSpan.Zero)
{
if (cancellationToken.IsCancellationRequested)
break;
var currentDelay = wait > maxDelay ? maxDelay : wait;
await Task.Delay(currentDelay, cancellationToken);
wait = wait.Subtract(currentDelay);
}
}
}
我编写了Delay函数来处理超过28天的延迟。
答案 1 :(得分:0)
您可以考虑使用.NET的反应式扩展并将其实现为Observables with a Timer和Cancellation Token
。使用Scheduler
,您可以确定最佳的方法线程方法(请参阅here)
下面的代码段可用于ExecuteAsync
方法中,该方法显示任意3秒的启动时间,然后具有60秒的到期日期(可以是任何时间长度。请注意Timestamp()
允许使用整数传递本地时间。
CancellationToken cancellationToken = CancellationToken.None;
Observable
.Timer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(60))
.Timestamp()
.ObserveOn(NewThreadScheduler.Default)
.Subscribe(
x =>
{
// do some task
} ,
cancellationToken);