BackgroundService 在计划外时间运行

BackgroundService runs on unscheduled times

我有这个后台服务:

public class ReminderService : BackgroundService
{

    private readonly int[] reminderDays = { 1, 3, 6, 9 };

    protected override async Task ExecuteAsync(CancellationToken stopToken)
    {
        while (!stopToken.IsCancellationRequested)
        {
            DateTime now = DateTime.Now;
            DateTime start = now.Date.AddHours(13);
            if (now > start) start = start.AddDays(1);
            await Task.Delay(start.Subtract(now), stopToken);
            if (reminderDays.Contains(now.Day)) await DoWork();
        }
    }

    protected async Task DoWork()
    {
        // The work...
    }

}

我希望服务 运行 每个月的第一天、第三天、第六天和第九天的下午 1 点。从我在日志中看到的情况来看,该服务很少 运行 比预期时间晚几毫秒,但通常会晚一天(下午 1 点左右的 2、4、7、10)。

if几乎只能在我的Task.Delay计算中,但我不知道为什么。

我有其他服务运行不同starts/delays但有同样的问题。

谁能看出哪里出了问题?我对其他方法持开放态度,但我不喜欢使用像 Hangfire 这样的库。

你可能会寻找一个调度程序quartz,它非常适合使用非常适合后台调度程序的crontab格式。

如果你想找个轻便的crontab库Cronos 或者NCrontab只能通过crontab获取时间

对于所有这些,我更喜欢使用 Cronos 库,因为它支持 UTC 和夏令时转换。

public class ReminderService : BackgroundService
{

    private const string cronExpression= "0 13 1,3,6,9 * *"; 
    private readonly CronExpression _cronJob;
    public ReminderService()
    {
      _cronJob = CronExpression.Parse(cronExpression);
    }

    protected override async Task ExecuteAsync(CancellationToken stopToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
          var now = DateTime.UtcNow;
          var nextUtc = _cronJob.GetNextOccurrence(now);
          await Task.Delay(now - nextUtc.Value, stoppingToken);
          await DoWork();
        }
    }

    protected async Task DoWork()
    {
        // The work...
    }
}

这是一个非常棒的网站 cron-expression-generator 帮助我们通过 UI

获取 crontab 表达式