[英]IHostedService/BackgroundService to run on a schedule (as opposed to Task.Delay)
Microsoft's example for a forever/continous IHostedService
at Implement background tasks in microservices with IHostedService and the BackgroundService class uses while
+ Task.Delay
'pattern'. Microsoft 使用IHostedService和BackgroundService类在微服务中实现后台任务的永远/连续IHostedService
的示例使用while
+ Task.Delay
'。 This illustrated with a code snippet that a simplified version is just below. 这用一个代码片段说明,简化版本就在下面。
public class GracePeriodManagerService : BackgroundService
(...)
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//Do work
await Task.Delay(timeSpan, stoppingToken);
}
}
This pattern suffers from a creeping shift - the work is done every timeSpan
+ how_long_work_took
. 这种模式从爬行转变遭受-工作完成每一个timeSpan
+ how_long_work_took
。 Even when how_long_work_took
is very small over a period of time it adds up. 即使how_long_work_took
在一段时间内非常小,它how_long_work_took
增加。
I would like to avoid calculating timeSpan
based on how long work
took. 我想避免根据work
时间计算timeSpan
。
What would a robust solution be to run every fixed_amount_of_time ? 每个fixed_amount_of_time运行一个强大的解决方案是什么? . 。
Thinking out loud: If I use a task scheduler library, like HangFire , inside ExecuteAsync
does using IHostedService
/ BackgroundService
even make sense any more? 大声思考:如果我使用像HangFire这样的任务调度程序库, 那么 ExecuteAsync
内部确实使用IHostedService
/ BackgroundService
甚至还有意义吗?
A bonus would be to be able to run a task at a point in time (eg at midnight) 奖励是能够在某个时间点(例如午夜)运行任务
This is how I handle such thing... In my case I need to start the service on specific day, specific hour and repeat every x days. 这就是我处理这种事情的方式......在我的情况下,我需要在特定日期,特定时间启动服务,并每隔x天重复一次。 But I don't know if it's what are you looking for exactly :) 但我不知道这是不是你在寻找什么:)
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);
}
}
}
I wrote Delay function to handle delays longer that 28 days. 我写了延迟函数来处理延迟28天的延迟。
You could consider using the Reactive extenstions for .NET and implement as Observables with a Timer and a Cancellation Token
. 您可以考虑使用.NET的Reactive扩展,并使用Timer和Cancellation Token
实现Observables 。 With a Scheduler
you can determine the best approach threading approach (refer here ) 使用Scheduler
您可以确定最佳方法线程方法(请参阅此处 )
The code snippet below could be used in the ExecuteAsync
method which shows an arbitrary 3 second startup time and then having a due date of 60 seconds (could be any time length. Note the Timestamp()
which allows passing of the local time with the integer. 下面的代码片段可以在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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.