我需要在特定时间触发托管服务,例如。每天 13:00 点。我的典型 ExecuteAsync 方法看起来像这样:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var interval = _configuration.IntervalInSeconds * 1000;
while (!stoppingToken.IsCancellationRequested)
{
// some job
await Task
.Delay(interval, stoppingToken);
}
}
当应用程序启动时,会调用 executeAsync 方法,然后每分钟都会执行一些操作。现在我必须只在特定时间执行这样的操作。有什么方法可以实现这种行为吗?除了粗略计算下一次触发时间外,我的问题还有什么解决办法吗??感谢您的帮助。
最佳答案
通过使用 Timer method ,您可以在特定时间运行任务,计算当前时间和目标时间之间的时间跨度,并将其作为 dueTime 参数传递。
代码如下:
public Task StartAsync(CancellationToken cancellationToken)
{
//DateTime.Today: 00:00:00
TimeSpan delayTime = DateTime.Today.AddHours(21) - DateTime.Now;
TimeSpan intervalTime = TimeSpan.FromMinutes(1); //86400000s == 1 day
_timer = new Timer(DoWork, null, delayTime, intervalTime);
return Task.CompletedTask;
}
此外,timer或 Task.Delay() methods更多的应用于指定时间间隔执行一个方法,如果你想实现定时任务,我建议你也可以尝试使用Cronos包和 Cron Expressions配置计划任务(引用:link)。
Cronos 包是一个轻量级但功能完备的库,用于解析 cron 表达式并计算下一次出现时区和夏令时。 Cronos 是 HangfireIO 赞助的开源项目,详细文档可以阅读 its GitHub repository . 详细步骤如下:
安装 Cronos通过 NuGet 打包。
使用以下代码创建一个 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(); } } public interface IScheduleConfig<T> { string CronExpression { get; set; } TimeZoneInfo TimeZoneInfo { get; set; } } public class ScheduleConfig<T> : IScheduleConfig<T> { public string CronExpression { get; set; } public TimeZoneInfo TimeZoneInfo { get; set; } } 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; } }
创建一个 ScheduleJob.cs:
public class ScheduleJob: CronJobService { private readonly ILogger<ScheduleJob> _logger; public ScheduleJob(IScheduleConfig<ScheduleJob> config, ILogger<ScheduleJob> logger) : base(config.CronExpression, config.TimeZoneInfo) { _logger = logger; } public override Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("ScheduleJob starts."); return base.StartAsync(cancellationToken); } public override Task DoWork(CancellationToken cancellationToken) { _logger.LogInformation($"{DateTime.Now:hh:mm:ss} ScheduleJob is working."); return Task.CompletedTask; } public override Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("ScheduleJob is stopping."); return base.StopAsync(cancellationToken); } }
在 ConfigureServices 方法中注册 ScheduleJob 服务。
public void ConfigureServices(IServiceCollection services) { services.AddHostedService<HelloWorldHostedService>(); services.AddCronJob<ScheduleJob>(c=> { c.TimeZoneInfo = TimeZoneInfo.Local; c.CronExpression = @"25 21 * * *"; // 21:25 PM daily. }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
然后结果如下:
关于c# - 如何在特定时间触发.NET Core 3.1 Hosted Service?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64005363/