2

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

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 任务调度程序之类的东西来做到这一点,或者我的方法很好,我吓坏了?

4

2 回答 2

2

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

  • 10 个时间表声称他们还有 1 秒的时间
  • elapsed 处理程序首先选择并以 1 秒的间隔重新启动
  • 它在 14:00 再次停止 - 就在第二天所有时间表都已经返回Next的时候

结果,您已经完成了 10 个工作中的 1 个。看起来不太好。

当然,您可以列出清单,或限制给定时间只能设置一项工作。但是话又说回来,您还需要做出哪些其他假设?schedules 数组可以为空吗?当有 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 负责所有的计时器,在请求的时间运行指定的作业等等。这很容易,有一套很好的教程,而且很好——你不需要实现任何东西。

于 2013-07-04T22:52:39.630 回答
1

在我看来,你的方法很好。实际上,它是一种灵感。我不会想出一个计时器并永久重新安排它。

我会做的——这完全是可选的,就像我说的那样,你的方法很好——只是让计时器每分钟触发一次,然后检查是否有事情要做。使用更小的时间分辨率,秒甚至毫秒,我不会那样做。但是让你的计时器每分钟运行一次,只检查内存中的数组,然后在 95% 的情况下直接进入睡眠状态,这并不是什么可怕的资源浪费。此外,它将允许代码更直接且更易于维护。

于 2013-07-04T22:20:31.893 回答