1. ホーム
  2. c#

[解決済み] C#のSystem.Threading.Timerが動作していないようです。3秒に一度、高速に動作します。

2022-09-19 11:13:48

質問

タイマーオブジェクトがあります。これを1分ごとに実行させたい。具体的には OnCallBack メソッドを実行し OnCallBack メソッドが実行されている間は非アクティブになります。いったん OnCallBack メソッドが終了すると、それは( OnCallBack ) がタイマーを再起動します。

今、私が持っているのはこんな感じです。

private static Timer timer;

private static void Main()
{
    timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
    Console.ReadLine();
}

private static void OnCallBack()
{
    timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
    Thread.Sleep(3000); //doing some long operation
    timer.Change(0, 1000 * 10);  //restarts the timer
}

しかし、どうもうまくいかないようです。3秒に1回の割合で非常に速く実行されます。ピリオド(1000*10)を上げてもです。を見て見ぬふりしているようです。 1000 * 10

何がいけなかったのでしょうか?

どうすればいいのでしょうか?

これはSystem.Threading.Timerの正しい使い方ではありません。Timerをインスタンス化するときは、ほとんどの場合、次のようにする必要があります。

_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );

このようにすると、タイマーは指定した時間が経過したときに一度だけ動作するようになります。そして、コールバック関数で、作業が完了したらタイマーを変更します。例

private void Callback( Object state )
{
    // Long running operation
   _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}

このように、同時並行性がないため、ロック機構は必要ない。タイマーは次のインターバルが経過した後、次のコールバックを起動します + 長時間実行された操作の時間。

正確にNミリ秒でタイマーを実行する必要がある場合は、ストップウォッチを使って長時間実行されている操作の時間を測定し、適切にChangeメソッドを呼び出すことをお勧めします。

private void Callback( Object state )
{
   Stopwatch watch = new Stopwatch();

   watch.Start();
   // Long running operation

   _timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}


I <強い 強く .NET を使っていて CLR を使っている人で Jeffrey Richter の本を読んでいない人には、ぜひとも C# を介した CLR を読んでいない人は、できるだけ早く読むことをお勧めします。タイマとスレッドプールについては、そこで非常に詳細に説明されています。