web-dev-qa-db-ja.com

.NETで1時間ごと(または1時間ごとに特定の時間間隔)にイベントを発生させるにはどうすればよいですか?

私は、システムトレイで実行され、1時間ごとにWebサイトをクロールする小さなWebクローラーに取り組んでいます。

.NETが1時間ごとにイベントを発生させたり、タスクを実行するために他の間隔を置いたりするための最良の方法は何ですか。たとえば、時間に基づいて20分ごとにイベントを実行します。イベントは次の場所で発生します。

00:20
00:40
01:00
01:20
01:40

等々。これを行うための最良の方法は、スレッドにループを作成することです。このループは、時間を一定の間隔で割り切れるかどうかを常にチェックし、時間に達するとコールバックイベントを発生させます。もっと良い方法がなければならないと感じています。

Timerを使用しますが、1時間に実行される「スケジュール」またはそれらの線に沿った何かに続くものを好むでしょう。

Windowsタスクスケジューラでアプリケーションを設定しなくても、これは可能ですか?

更新:
タイマーの時間間隔を計算するアルゴリズムを追加しています。このメソッドは、「minute」パラメーターを取ります。これは、タイマーがティックをトリガーする時間です。たとえば、「minute」パラメータが20の場合、タイマーは上記のタイムテーブルの間隔でティックします。

int CalculateTimerInterval(int minute)
{
    if (minute <= 0)
        minute = 60;
    DateTime now = DateTime.Now;

    DateTime future = now.AddMinutes((minute - (now.Minute % minute))).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);

    TimeSpan interval = future - now;

    return (int)interval.TotalMilliseconds;
}

このコードは次のように使用されます。

static System.Windows.Forms.Timer t;
const int CHECK_INTERVAL = 20;


static void Main()
{
    t = new System.Windows.Forms.Timer();
    t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
    t.Tick += new EventHandler(t_Tick);
    t.Start();
}

static void t_Tick(object sender, EventArgs e)
{
    t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
}
30
Dan Herbert

System.Timers.Timer 。 1日の特定の時間に実行する場合は、次回までの時間を把握し、それを間隔として設定する必要があります。

これは単なる基本的な考え方です。必要な精度に応じて、さらに多くのことができます。

int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;  
31
tvanfosson

Quartz.netからヘルプを見つけることができます http://quartznet.sourceforge.net/

11
suhair

スレッドタイミングと非同期呼び出しを使用する軽量システムの例を次に示します。

欠点があることはわかっていますが、長時間実行されるプロセス(スケジュールされたバックエンドサービスなど)を開始するときに、タイマーの代わりにこれを使用するのが好きです。タイマースレッドでインラインで実行されるため、元の呼び出しが完了する前に再び起動されることを心配する必要はありません。これは、日付時刻の配列をトリガー時刻として使用したり、さらに機能を追加したりするために、かなり拡張することができます。皆さんの中にはもっと良い方法を知っている人もいると思います。

    public Form1()
    {
        InitializeComponent();

        //some fake data, obviously you would have your own.
        DateTime someStart = DateTime.Now.AddMinutes(1);
        TimeSpan someInterval = TimeSpan.FromMinutes(2);

        //sample call
        StartTimer(someStart,someInterval,doSomething);
    }

    //just a fake function to call
    private bool doSomething()
    {
        DialogResult keepGoing = MessageBox.Show("Hey, I did something! Keep Going?","Something!",MessageBoxButtons.YesNo);
        return (keepGoing == DialogResult.Yes);
    }

    //The following is the actual guts.. and can be transplanted to an actual class.
    private delegate void voidFunc<P1,P2,P3>(P1 p1,P2 p2,P3 p3);
    public void StartTimer(DateTime startTime, TimeSpan interval, Func<bool> action)
    {
        voidFunc<DateTime,TimeSpan,Func<bool>> Timer = TimedThread;
        Timer.BeginInvoke(startTime,interval,action,null,null);
    }

    private void TimedThread(DateTime startTime, TimeSpan interval, Func<bool> action)
    {
        bool keepRunning = true;
        DateTime NextExecute = startTime;
        while(keepRunning)
        {
            if (DateTime.Now > NextExecute)
            {
                keepRunning = action.Invoke();
                NextExecute = NextExecute.Add(interval);
            }
            //could parameterize resolution.
            Thread.Sleep(1000);
        }            
    }
4
ExCodeCowboy

このためのもう1つの戦略は、プロセスが実行された最終時刻を記録し、その時刻から目的の間隔が経過したかどうかを判断することです。この戦略では、経過時間がOR GREATER THAN希望の間隔に等しい場合に発生するようにイベントをコーディングします。このようにして、長い間隔(1日1回、例)何らかの理由でコンピューターがダウンした場合、見逃される可能性があります。

たとえば、次のとおりです。

  • lastRunDateTime = 2009年5月2日午後8時
  • 24時間ごとにプロセスを実行したい
  • タイマーイベントで、プロセスが最後に実行されてから24時間OR MOREが経過したかどうかを確認します。
  • はいの場合、プロセスを実行し、必要な間隔を追加してlastRunDateTimeを更新します(この場合は24時間ですが、必要なものは何でも)

明らかに、システムがダウンした後にこれを回復するためには、lastRunDateTimeをファイルまたはデータベースのどこかに保存して、プログラムが回復時に中断したところから再開できるようにする必要があります。

2
Casey Gay

System.Windows.Forms.Timer(またはSystem.Timers.Timer)

しかし、タイマーを使用したくないと言ったので、別のスレッドで軽量の待機プロセスを実行するか(時間を確認し、数秒スリープし、再度時間を確認します...)、イベントを発生させるコンポーネントを作成します(使用する特定のスケジュールされた時間または間隔での軽量の待機プロセス)

1
Steven A. Lowe