IHostedService/BackgroundService 按计划运行

Microsoft在使用 IHostedService 和 BackgroundService 类在微服务IHostedService中实现后台任务的永久/连续示例使用while+ Task.Delay'pattern'。这用代码片段说明,简化版本就在下面。


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甚至更有意义吗?


奖励是能够在某个时间点(例如午夜)运行任务


慕尼黑8549860
浏览 301回答 2
2回答

慕尼黑的夜晚无繁华

这就是我处理此类事情的方式......在我的情况下,我需要在特定日期、特定时间启动服务并每隔 x 天重复一次。但我不知道这是否正是您要找的东西:)public class ScheduleHostedService: BackgroundService{&nbsp; &nbsp; private readonly ILogger<ScheduleHostedService> _logger;&nbsp; &nbsp; private readonly DaemonSettings _settings;&nbsp; &nbsp; public ScheduleHostedService(IOptions<DaemonSettings> settings, ILogger<ScheduleHostedService> logger)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _logger = logger;&nbsp; &nbsp; &nbsp; &nbsp; _settings = settings.Value;&nbsp; &nbsp; }&nbsp; &nbsp; protected override async Task ExecuteAsync(CancellationToken stoppingToken)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; DateTime? callTime=null;&nbsp; &nbsp; &nbsp; &nbsp; if (_settings.StartAt.HasValue)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DateTime next = DateTime.Today;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; next = next.AddHours(_settings.StartAt.Value.Hour)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .AddMinutes(_settings.StartAt.Value.Minute)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .AddSeconds(_settings.StartAt.Value.Second);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (next < DateTime.Now)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; next = next.AddDays(1);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callTime = next;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if (_settings.StartDay.HasValue)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callTime = callTime ?? DateTime.Now;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callTime = callTime.Value.AddDays(-callTime.Value.Day).AddDays(_settings.StartDay.Value);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (callTime < DateTime.Now)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callTime = callTime.Value.AddMonths(1);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if(callTime.HasValue)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await Delay(callTime.Value - DateTime.Now, stoppingToken);&nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callTime = DateTime.Now;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; while (!stoppingToken.IsCancellationRequested)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //do smth&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var nextRun = callTime.Value.Add(_settings.RepeatEvery) - DateTime.Now;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await Delay(nextRun, stoppingToken);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; static async Task Delay(TimeSpan wait, CancellationToken cancellationToken)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var maxDelay = TimeSpan.FromMilliseconds(int.MaxValue);&nbsp; &nbsp; &nbsp; &nbsp; while (wait > TimeSpan.Zero)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (cancellationToken.IsCancellationRequested)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var currentDelay = wait > maxDelay ? maxDelay : wait;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await Task.Delay(currentDelay, cancellationToken);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wait = wait.Subtract(currentDelay);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}我编写了 Delay 函数来处理超过 28 天的延迟。

慕斯709654

您可以考虑使用 .NET 的 Reactive 扩展,并将其实现为带有 Timer和Cancellation Token. 使用 aScheduler您可以确定最佳线程方法(请参阅此处)下面的代码片段可用于ExecuteAsync显示任意 3 秒启动时间然后具有 60 秒到期日期的方法(可以是任何时间长度。注意Timestamp()它允许使用整数传递本地时间。CancellationToken cancellationToken = CancellationToken.None;Observable&nbsp; .Timer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(60))&nbsp; .Timestamp()&nbsp; .ObserveOn(NewThreadScheduler.Default)&nbsp; .Subscribe(&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; x =>&nbsp; &nbsp; &nbsp; &nbsp;{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // do some task&nbsp; &nbsp; &nbsp; &nbsp;} ,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; cancellationToken);
打开App,查看更多内容
随时随地看视频慕课网APP