3

我有以下计时器可以在 .Net 核心 Ihosted 服务中运行,

TimeSpan ScheduledTimespan;
string[] formats = { @"hh\:mm\:ss", "hh\\:mm" };
string strTime = Startup.Configuration["AppSettings:JobStartTime"].ToString();
var success = TimeSpan.TryParseExact(strTime, formats, CultureInfo.InvariantCulture, out ScheduledTimespan);
Timer _timer = new Timer(JobToRun, null, TimeSpan.Zero, ScheduledTimespan);

我正在使用这个特定的重载,

public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period);

但是JobToRun一旦控制到达它就会执行。如何让它每天在一天中的特定时间运行?提前致谢。

4

2 回答 2

9

以下函数返回解析的作业运行时间

private static TimeSpan getScheduledParsedTime()
{
     string[] formats = { @"hh\:mm\:ss", "hh\\:mm" };
     string jobStartTime = "07:10";
     TimeSpan.TryParseExact(jobStartTime, formats, CultureInfo.InvariantCulture, out TimeSpan ScheduledTimespan);
     return ScheduledTimespan;
}

以下函数返回当前时间的延迟时间。如果一天中的当前时间超过了作业运行时间,则会向作业运行添加适当的延迟,如下所示,

private static TimeSpan getJobRunDelay()
{
    TimeSpan scheduledParsedTime = getScheduledParsedTime();
    TimeSpan curentTimeOftheDay = TimeSpan.Parse(DateTime.Now.TimeOfDay.ToString("hh\\:mm"));
    TimeSpan delayTime = scheduledParsedTime >= curentTimeOftheDay
        ? scheduledParsedTime - curentTimeOftheDay
        : new TimeSpan(24, 0, 0) - curentTimeOftheDay + scheduledParsedTime;
    return delayTime;
}

下面的计时器将methodToExecute根据JobRunDelay每天调用

_timer = new Timer(methodToExecute, null, getJobRunDelay(), new TimeSpan(24, 0, 0));
于 2020-01-14T08:46:48.460 回答
8

使用 Cron 作业

  1. 创建 ASP.NET Core Web 项目

  2. 添加 NuGet 包 Microsoft.Extensions.Hosting

  3. 添加 NuGet 包 Cronos

  4. Crete 抽象类 CronJobService - 在这个 CronJobService 中,我们使用计时器来跟踪时间,并在时间达到计划时触发后台任务

    public abstract class CronJobService : IHostedService, IDisposable
    {
      private System.Timers.Timer _timer;
      private readonly CronExpression _expression;
      private readonly TimeZoneInfo _timeZoneInfo;
    
      protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo)
      {
        _expression = CronExpression.Parse(cronExpression);
        _timeZoneInfo = timeZoneInfo;
      }
    
      public virtual async Task StartAsync(CancellationToken cancellationToken)
      {
       await ScheduleJob(cancellationToken);
      }
    
      protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
      {
         var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo);
         if (next.HasValue)
         {
           var delay = next.Value - DateTimeOffset.Now;
           if (delay.TotalMilliseconds <= 0)   // prevent non-positive values from being passed into Timer
           {
              await ScheduleJob(cancellationToken);
           }
           _timer = new System.Timers.Timer(delay.TotalMilliseconds);
           _timer.Elapsed += async (sender, args) =>
           {
              _timer.Dispose();  // reset and dispose timer
              _timer = null;
    
              if (!cancellationToken.IsCancellationRequested)
              {
                  await DoWork(cancellationToken);
              }
    
              if (!cancellationToken.IsCancellationRequested)
              {
                  await ScheduleJob(cancellationToken);    // reschedule next
              }
           };
           _timer.Start();
         }
         await Task.CompletedTask;
      }
    
      public virtual async Task DoWork(CancellationToken cancellationToken)
      {
         await Task.Delay(5000, cancellationToken);  // do the work
      }
    
      public virtual async Task StopAsync(CancellationToken cancellationToken)
      {
        _timer?.Stop();
        await Task.CompletedTask;
      }
    
      public virtual void Dispose()
      {
        _timer?.Dispose();
      }
    }
    
  5. 从 CronJobService 继承

      public class XXXCronJob:CronJobService
      {
        private readonly ILogger<CronJob> _logger;
    
        public XXXCronJob(IScheduleConfig<CronJob > config, ILogger<CronJob> logger)
        : base(config.CronExpression, config.TimeZoneInfo)
        {
         _logger = logger;
        }
    
        public override Task StartAsync(CancellationToken cancellationToken)
        {
         _logger.LogInformation("CronJob  starts.");
         return base.StartAsync(cancellationToken);
        }
    
        public override Task DoWork(CancellationToken cancellationToken)
        {
         _logger.LogInformation($"{DateTime.Now:hh:mm:ss} CronJob  is working.");
         return Task.CompletedTask;
        }
    
        public override Task StopAsync(CancellationToken cancellationToken)
        {
         _logger.LogInformation("CronJob  is stopping.");
         return base.StopAsync(cancellationToken);
        }
     }
    
  6. 添加新接口 IScheduleConfig

     public interface IScheduleConfig<T>
     {
       string CronExpression { get; set; }
       TimeZoneInfo TimeZoneInfo { get; set; }
     }
    
  7. 实施 IScheduleConfig

     public class ScheduleConfig<T> : IScheduleConfig<T>
     {
       public string CronExpression { get; set; }
       public TimeZoneInfo TimeZoneInfo { get; set; }
     }
    
  8. 添加扩展类 ScheduledServiceExtensions

     public static class ScheduledServiceExtensions
     {
        public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobService
        {
           if (options == null)
           {
              throw new ArgumentNullException(nameof(options), @"Please provide Schedule Configurations.");
           }
           var config = new ScheduleConfig<T>();
           options.Invoke(config);
           if (string.IsNullOrWhiteSpace(config.CronExpression))
           {
              throw new ArgumentNullException(nameof(ScheduleConfig<T>.CronExpression), @"Empty Cron Expression is not allowed.");
           }
    
           services.AddSingleton<IScheduleConfig<T>>(config);
           services.AddHostedService<T>();
          return services;
        }
     }
    
  9. 使用泛型安排多个 Cron 作业

     public void ConfigureServices(IServiceCollection services)
     {
       services.AddCronJob<XXXCronJob>(c =>
       {
         c.TimeZoneInfo = TimeZoneInfo.Local;
         c.CronExpression = @"00 05 * * *";
       });
       services.AddCronJob<YYYCronJob>(c =>
       {
         c.TimeZoneInfo = TimeZoneInfo.Local;
         c.CronExpression = @"00 08 * * *";
       });
       services.AddCronJob<ZZZCronJob>(c =>
       {
         c.TimeZoneInfo = TimeZoneInfo.Local;
         c.CronExpression = @"30 14 * * *";
       });
     }
    

    第一个是 05:00 AM。

    第二个是 08:00 AM

    最后一个是下午 14:30。

于 2020-11-14T20:17:27.963 回答