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 秒あると主張している
  • 経過したハンドラーが最初に選択し、1 秒間隔でそれ自体を再起動します
  • 14:00 に再び停止します。ちょうど、Next翌日のすべてのスケジュールがすでに戻ってきているときです。

その結果、10 件のジョブのうち 1 件を実行しました。よく見えません。

もちろん、リストを取得したり、特定の時間に 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 は、要求された時間に指定されたジョブを実行するなど、すべてのタイマーを処理します。簡単で、優れたチュートリアルのセットがあり、何も実装する必要はありません。

于 2013-07-04T22:52:39.630 に答える
1

私の意見では、あなたのアプローチは問題ありません。実際、それは一種のインスピレーションです。タイマーを 1 つ用意して、それを永久に再スケジュールすることは思いつきませんでした。

私がしたことは、これは厳密にオプションです。私が言ったように、あなたのアプローチは問題ありません-タイマーを毎分起動させ、何かすることがあるかどうかを確認するだけです。より小さな時間分解能、秒またはミリ秒でさえ、私はそれをしません。しかし、タイマーを毎分実行し、メモリ内配列をチェックするだけで、95% のケースですぐにスリープ状態に戻ることは、リソースのひどい浪費にはなりません。さらに、はるかに単純で保守しやすいコードが可能になります。

于 2013-07-04T22:20:31.893 に答える