114

タイマーオブジェクトがあります。毎分実行してほしい。具体的には、OnCallBackメソッドを実行する必要があり、メソッドの実行中に非アクティブになりOnCallBackます。OnCallBackメソッドが終了すると、(a )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秒ごとに非常に高速に実行されます。ピリオド(1000 * 10)を上げても。目をつぶってしまうようです1000 * 10

私は何を間違えましたか?

4

5 に答える 5

239

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

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

これにより、間隔が経過したときに1回だけティックするようにタイマーに指示されます。次に、コールバック関数で、作業が完了する前ではなく、完了したらタイマーを変更します。例:

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

したがって、同時実行性がないため、ロックメカニズムは必要ありません。タイマーは、次の間隔が経過した後、次のコールバックを起動します+長時間実行されている操作の時間。

タイマーを正確にNミリ秒で実行する必要がある場合は、Stopwatchを使用して長時間実行操作の時間を測定し、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 );
}

.NETを実行していて、Jeffrey Richterの本を読んでいないCLR(C#経由のCLR)を使用している人は、できるだけ早く読むことを強くお勧めします。タイマーとスレッドプールについては、ここで詳しく説明されています。

于 2012-10-09T09:54:35.833 に答える
14

タイマーを停止する必要はありません。この投稿からの素晴らしい解決策を参照してください

「タイマーにコールバックメソッドの起動を継続させることはできますが、再入可能でないコードをMonitor.TryEnter / Exitでラップします。その場合、タイマーを停止/再起動する必要はありません。重複する呼び出しはロックを取得せず、すぐに戻りません。」

private void CreatorLoop(object state) 
 {
   if (Monitor.TryEnter(lockObject))
   {
     try
     {
       // Work here
     }
     finally
     {
       Monitor.Exit(lockObject);
     }
   }
 }
于 2012-10-09T08:52:15.970 に答える
9

使用はSystem.Threading.Timer必須ですか?

そうでない場合は、System.Timers.Timer便利なメソッドがStart()ありStop()ます(また、AutoResetfalseに設定できるプロパティがあるため、Stop()は不要で、Start()実行後に呼び出すだけです)。

于 2012-10-09T09:22:32.597 に答える
3

私はただするだろう:

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

  private static void OnCallBack()
  {
    timer.Dispose();
    Thread.Sleep(3000); //doing some long operation
    timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
  }

また、周期性を自分で制御しようとしているため、周期パラメータは無視してください。


0パラメータを指定し続けるため、元のコードは可能な限り高速に実行されdueTimeます。差出人Timer.Change

dueTimeがゼロ(0)の場合、コールバックメソッドはすぐに呼び出されます。

于 2012-10-09T08:53:50.717 に答える
0
 var span = TimeSpan.FromMinutes(2);
 var t = Task.Factory.StartNew(async delegate / () =>
   {
        this.SomeAsync();
        await Task.Delay(span, source.Token);
  }, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

source.Cancel(true/or not);

// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)

あなたが好きなら、内部でいくつかのループスレッドを使用してください...

public async void RunForestRun(CancellationToken token)
{
  var t = await Task.Factory.StartNew(async delegate
   {
       while (true)
       {
           await Task.Delay(TimeSpan.FromSeconds(1), token)
                 .ContinueWith(task => { Console.WriteLine("End delay"); });
           this.PrintConsole(1);
        }
    }, token) // drop thread options to default values;
}

// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.
于 2017-07-20T15:42:56.033 に答える