私は、システムトレイで実行され、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);
}
System.Timers.Timer 。 1日の特定の時間に実行する場合は、次回までの時間を把握し、それを間隔として設定する必要があります。
これは単なる基本的な考え方です。必要な精度に応じて、さらに多くのことができます。
int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;
Quartz.netからヘルプを見つけることができます http://quartznet.sourceforge.net/
スレッドタイミングと非同期呼び出しを使用する軽量システムの例を次に示します。
欠点があることはわかっていますが、長時間実行されるプロセス(スケジュールされたバックエンドサービスなど)を開始するときに、タイマーの代わりにこれを使用するのが好きです。タイマースレッドでインラインで実行されるため、元の呼び出しが完了する前に再び起動されることを心配する必要はありません。これは、日付時刻の配列をトリガー時刻として使用したり、さらに機能を追加したりするために、かなり拡張することができます。皆さんの中にはもっと良い方法を知っている人もいると思います。
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);
}
}
このためのもう1つの戦略は、プロセスが実行された最終時刻を記録し、その時刻から目的の間隔が経過したかどうかを判断することです。この戦略では、経過時間がOR GREATER THAN希望の間隔に等しい場合に発生するようにイベントをコーディングします。このようにして、長い間隔(1日1回、例)何らかの理由でコンピューターがダウンした場合、見逃される可能性があります。
たとえば、次のとおりです。
明らかに、システムがダウンした後にこれを回復するためには、lastRunDateTimeをファイルまたはデータベースのどこかに保存して、プログラムが回復時に中断したところから再開できるようにする必要があります。
System.Windows.Forms.Timer(またはSystem.Timers.Timer)
しかし、タイマーを使用したくないと言ったので、別のスレッドで軽量の待機プロセスを実行するか(時間を確認し、数秒スリープし、再度時間を確認します...)、イベントを発生させるコンポーネントを作成します(使用する特定のスケジュールされた時間または間隔での軽量の待機プロセス)