咨询区
Behrooz Karjoo
我的应用程序需要做一个 事件触发
的功能,它需要每天定时执行,比如说当天的 16点,我现在的做法是使用一个 timer 按秒轮询判断当前是否为 16:00
, 虽然可以玩得转,但我想能不能实现那种 16:00
自动触发回调函数的模式,而不是现在无时无刻的轮询。
回答区
noontz
其实很简单,计算触发时间与当前时间的差值,然后将 差值
作为精确的延迟时间,带入到 Task.Delay
中即可,外面再套个 while(true)
,下面是我封装的代码。
/// <summary>
/// Utility class for triggering an event every 24 hours at a specified time of day
/// </summary>
public class DailyTrigger : IDisposable
{/// <summary>/// Time of day (from 00:00:00) to trigger/// </summary>TimeSpan TriggerHour { get; }/// <summary>/// Task cancellation token source to cancel delayed task on disposal/// </summary>CancellationTokenSource CancellationToken { get; set; }/// <summary>/// Reference to the running task/// </summary>Task RunningTask { get; set; }/// <summary>/// Initiator/// </summary>/// <param name="hour">The hour of the day to trigger</param>/// <param name="minute">The minute to trigger</param>/// <param name="second">The second to trigger</param>public DailyTrigger(int hour, int minute = 0, int second = 0){TriggerHour = new TimeSpan(hour, minute, second);CancellationToken = new CancellationTokenSource();RunningTask = Task.Run(async () => {while (true){var triggerTime = DateTime.Today + TriggerHour - DateTime.Now;if (triggerTime < TimeSpan.Zero)triggerTime = triggerTime.Add(new TimeSpan(24, 0, 0));await Task.Delay(triggerTime, CancellationToken.Token);OnTimeTriggered?.Invoke();}}, CancellationToken.Token);}/// <inheritdoc/>public void Dispose(){CancellationToken?.Cancel();CancellationToken?.Dispose();CancellationToken = null;RunningTask?.Dispose();RunningTask = null;}/// <summary>/// Triggers once every 24 hours on the specified time/// </summary>public event Action OnTimeTriggered;/// <summary>/// Finalized to ensure Dispose is called when out of scope/// </summary>~DailyTrigger() => Dispose();
}
然后像下面这样使用。
void Main()
{var trigger = new DailyTrigger(16); // every day at 4:00pmtrigger.OnTimeTriggered += () => {// Whatever}; Console.ReadKey();
}
点评区
思路是个好思路,不过在正式的项目开发中,建议还是用 Quartz.NET
或者 HangFire
这种专业的调度框架,毕竟它支持强大的 Cron 表达式。
RecurringJob.AddOrUpdate(() => MyMethod(), "* 16 * * *");