为了账号安全,请及时绑定邮箱和手机立即绑定

IHostedService/BackgroundService 按计划运行

IHostedService/BackgroundService 按计划运行

C#
慕尼黑8549860 2021-11-14 10:46:04
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甚至更有意义吗?奖励是能够在某个时间点(例如午夜)运行任务
查看完整描述

2 回答

?
慕尼黑的夜晚无繁华

TA贡献1864条经验 获得超6个赞

这就是我处理此类事情的方式......在我的情况下,我需要在特定日期、特定时间启动服务并每隔 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 天的延迟。


查看完整回答
反对 回复 2021-11-14
?
慕斯709654

TA贡献1840条经验 获得超5个赞

您可以考虑使用 .NET 的 Reactive 扩展,并将其实现为带有 Timer和Cancellation Token. 使用 aScheduler您可以确定最佳线程方法(请参阅此处)


下面的代码片段可用于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);


查看完整回答
反对 回复 2021-11-14
  • 2 回答
  • 0 关注
  • 300 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信