いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Timerのコールバック処理に時間がかかった場合でも順に呼び出しを行う (System.Threading.Timer, Timer.Change, Timeout.Infinite)


知ってると、ちょっと便利かもしれないTips。メモメモ。


System.Threading.Timerを利用する際に、コールバックメソッド内にて
時間がかかる処理があったりすると、そのコールバックの処理を行っている最中に
もう一つのコールバックが呼び出されたりします。たとえば、コールバックの呼び出し間隔が
500ミリ秒ごとであった場合に、処理自体が700ミリ秒かかってしまった時などです。


以下、重複する呼び出しが行われるサンプル。

  #region ThreadingNamespaceSamples-05
  public class ThreadingNamespaceSamples05 : IExecutable
  {
    public void Execute()
    {
      var timer = new System.Threading.Timer(TimerCallback);
      timer.Change(0, 500);
      
      Thread.Sleep(10000);
    }
    
    void TimerCallback(object state)
    {
      Console.WriteLine("Timer Callback!!");
      
      var rnd = new Random();
      
      // 時間のかかる処理をシミュレート
      Thread.Sleep(rnd.Next(1000));
      Console.WriteLine("\tsleep done.");
    }
  }
  #endregion


実行すると以下のようになります。

  Timer Callback!!
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!


通常、このような状態になっても大丈夫な事が多いのですが、場合によっては重複せずに
順に呼び出されてほしい時もあります。つまり、処理に時間がかかった場合、その追加時間分
タイマーのコールバックの呼び出しも遅延してほしい時です。


以下、順に呼び出されるように修正したサンプルです。

  #region ThreadingNamespaceSamples-05
  public class ThreadingNamespaceSamples05 : IExecutable
  {
    public void Execute()
    {
      //
      // 普通にスレッドタイマーを作成し、コールバックの呼び出し間隔を無効に
      // した状態でタイマーを開始させる.
      //
      var timer = new System.Threading.Timer(TimerCallback);
      timer.Change(0, Timeout.Infinite);
      
      Thread.Sleep(10000);
    }
    
    void TimerCallback(object state)
    {
      Console.WriteLine("Timer Callback!!");
      
      var rnd = new Random();
      
      // 時間のかかる処理をシミュレート
      Thread.Sleep(rnd.Next(1000));
      Console.WriteLine("\tsleep done.");
      
      //
      // 再度Changeメソッドを呼び出して、次のコールバックを設定.
      //
      var timer = state as System.Threading.Timer;
      timer.Change(rnd.Next(700), Timeout.Infinite);
    }
  }
  #endregion


実行結果は、以下のようになります。

  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!
  	sleep done.
  Timer Callback!!


順に呼び出されるようになっているのがわかります。
2番目のサンプルでは、最初に

timer.Change(0, Timeout.Infinite);

として、コールバックメソッドを呼び出す間隔を無効にしています。
つまり、この設定にするとコールバックは一回しか呼ばれません。


次にコールバックメソッドにて、処理完了後、再度Changeメソッドを
利用して、次のコールバックを設定しています。



================================
過去の記事については、以下のページからご参照下さい。