c# - IHostedService/BackgroundService 按计划运行(与 Task.Delay 相反)

标签 c# .net .net-core scheduling

Microsoft 的永久/连续 IHostedService 示例,位于 Implement background tasks in microservices with IHostedService and the BackgroundService class使用 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 在一段时间内非常小,它也会加起来。

我想避免根据work 花费的时间来计算timeSpan

每隔 fixed_amount_of_time 运行一次的可靠解决方案是什么?

大声思考:如果我使用任务调度程序库,例如 HangFire ,在 ExecuteAsync 中使用 IHostedService/BackgroundService 是否更有意义?

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

最佳答案

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

我编写了延迟函数来处理超过 28 天的延迟。

关于c# - IHostedService/BackgroundService 按计划运行(与 Task.Delay 相反),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51667000/

相关文章:

c# - 在 Xamarin 的 View 中将 ViewModel 属性与 ListView 绑定(bind)

c# - 使用 protobuf-net,序列化实现接口(interface)的派生类型的正确方法是什么?

.net - 在用户控件上设置绑定(bind)到主窗口的 ViewModel

c# - 为什么使用 JWT 时 UserManager.GetUserAsync 会返回 null?

c# - 在 .NET Core 3.0 中从 C# 执行提升的 powershell 脚本

c# - 如何在 EPPlus 中使用 C# 删除 AutoFilter

c# - 从 C# 获取正在运行的 Java 程序的名称(如 .NET 应用程序的进程名称)

c# - 为基于声明的安全性实现自定义属性

javascript - 如何使用react获取html格式的表格?

c# - 返回 HttpResponseMessage 并且配置了 Application Insights 时,WebAPI 响应永远不会完成