在Windows服务内的计划中运行方法

本文关键字:运行 方法 计划 Windows 服务 | 更新日期: 2023-09-27 18:21:09

我有一个Windows服务,我需要在特定的Schedules中运行一个方法。到目前为止,我实现了一个表示时间表的类。

public class SchaduleTime
{
    public int Hour { get; set; }
    public int Minute { get; set; }
    public DateTime Next
    {
        get
        {
            var now = DateTime.Now;
            var dt = now.Date;
            // the time has passed to execute today?
            if (Hour * 60 + Minute < now.Hour * 60 + now.Minute)
            {
                dt = dt.AddDays(1);
            }
            return new DateTime(dt.Year, dt.Month, dt.Day, Hour, Minute, 0);
        }
    }
}

我创建了一个包含以下字段的主类:

System.Timers.Timer timer;
private SchaduleTime[] schadules;

并在计时器字段的Elapsed事件中运行类似的操作:

private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // do my work.
    // programing next:
    var nowTicks = DateTime.Now.Ticks;
    // get the next schadule.
    var next = schadules
        .Select(s => new
            {
                Schadule = s,
                IntervalNeeded = s.Next.Ticks - nowTicks
            })
        .OrderBy(o => o.IntervalNeeded)
        .First();
    timer.Enabled = false;
    timer.Stop();
    timer.Interval = (int) new TimeSpan(next.IntervalNeeded).TotalMilliseconds;
    timer.Enabled = true;
    timer.Start();
}

对我来说,这似乎是一个拙劣的策略或意大利面条代码,我的意思是看起来很丑陋。

有没有一种方法可以使用一个专门的类来实现这一点,该类可以处理调度或类似于.net中的Windows任务调度程序,或者我的方法很好,我吓坏了?

在Windows服务内的计划中运行方法

让我们考虑一下假设的情况,您在14:00有10个时间表。假设你的计时器在13:59:59停止,结果是:

  • 10个时间表声称他们还有1秒
  • 已运行的处理程序先拾取,然后每隔1秒重新启动
  • 它在14:00再次停止,此时所有时间表都已返回第二天的Next

因此,你已经完成了10份工作中的1份。看起来不太好。

当然,你们可以选择列表,或者限制在给定的时间内只能设置一个作业。但话说回来,你还需要做出哪些其他假设?时间表数组可以为空吗?当有100万个时间表时,它能工作吗?你有这个代码的测试吗?等等。

相反,您可以使用专门的库来执行,也就是-Quartz。这是一个简单的作业调度程序,它肯定可以做你想在这里实现的事情:

ISchedulerFactory factory= new StdSchedulerFactory();
IScheduler scheduler = factory.GetScheduler();
scheduler.Start();
// You'll have to implement class performing actual work to be done - ServiceJob
JobDetail jobDetail = new JobDetail("ServiceJob", null, typeof(ServiceJob));
Trigger trigger = TriggerUtils.MakeDailyTrigger();
// Start time could be anytime today
trigger.StartTimeUtc = DateTime.UtcNow;
trigger.Name = "ServiceTrigger";
scheduler.ScheduleJob(jobDetail, trigger);

Quartz负责所有计时器,在请求的时间运行指定的作业,等等。它很简单,有一套很好的教程,而且你不需要实现任何东西。

在我看来,您的方法很好。事实上,这是一种启发。我不会想出一个定时器并永久重新安排它。

我会做的是——这是严格可选的,就像我说的,你的方法很好——只是让计时器每分钟启动一次,然后检查是否有什么事情要做。如果时间分辨率较小,几秒甚至几毫秒,我不会这么做。但是,让你的计时器每分钟运行一次,只检查内存中的数组,在95%的情况下直接返回睡眠状态,并不是对资源的可怕浪费。此外,它还允许代码更加简单明了,更易于维护。