2011-08-14 1 views
5

私はすべてx秒の呼び出しを行う関数がありますが、スレッドセーフであることが必要です。非リエントラントタイマー

タイマーを作成するときにこの動作を設定できますか? (.NETのタイマーを使用するのは気にしないですが、スレッドセーフでなければなりません)。

コールバック関数内でロックを実装できることがわかっていますが、タイマーレベルであればより優雅になると思います。

私のコールバック関数と環境はUIに関連していません。

[編集1] コールバック関数内に複数のスレッドが存在しないようにしたいだけです。

ここでは、[編集2] タイマーは私のコールバックを呼び出すときに責任があるので、私は、タイマーレベルの内側のロックを維持したい、と私は私を呼び出すにしたくないときに、特定の状況がありますコールバック関数。だから私はは、タイマーの責任において呼び出すと思います。

+2

あなたが十分な情報を与えていない...は、プログラムの他の部分にはアクセスし、あなたの関数へのアクセスは何もしていますか?そうでない場合は、すでにスレッドセーフです(タイマーの有無にかかわらず)。はいの場合、タイマーはスレッドセーフでは何もありませんが、ロックなどを使用する必要があります。 – Yahia

+1

**なぜ**ロックアウトを保持しますか?コールバック関数の正確な? –

+0

@Paul - 私の編集を参照してください2 – Delashmate

答えて

16

あなたの質問が完全ではないので、あなたがコールバックを処理している間にタイマーがあなたのコールバックを再入力できないようにしたいと思っています。 System.Timers.Timerを使用し、AutoResetプロパティがfalseに設定されていることを確認してください。これは、あなたがこのように任意の再入を防止し、手動で各区間のタイマーをトリガするために持っていることを確認します:

public class NoLockTimer : IDisposable 
{ 
    private readonly Timer _timer; 

    public NoLockTimer() 
    { 
     _timer = new Timer { AutoReset = false, Interval = 1000 }; 

     _timer.Elapsed += delegate 
     { 
      //Do some stuff 

      _timer.Start(); // <- Manual restart. 
     }; 

     _timer.Start(); 
    } 

    public void Dispose() 
    { 
     if (_timer != null) 
     { 
      _timer.Dispose(); 
     } 
    } 
} 
+0

あなたは私のクエストインを完全に理解し、あなたの解決策を気に入っていました。タイマークラスの内部にこの行動があることを期待しましたが、これは十分です。 – Delashmate

+0

これは私がやったことです。 FYI - 匿名の代理人を使用しなかった場合、この例は少し明確になります。 –

2

私は私が私のコールバック関数の内部でロックを実装することができます知っているが、私はそれはロックが必要であれば、どのようにタイマーがそれを手配することができ、タイマーレベル

になる場合、それは、よりエレガントになると思います?あなたは魔法の空想を探しています。

再EDIT1:

選択肢は両方とも、でSystem.Timers.TimerとSystem.Threading.Timerある再入に対する予防措置を必要とします。 this pageを参照して、タイマーイベント再入荷の処理セクションを参照してください。

+0

私の編集1を参照してください、それはあなたに合っていますか? – Delashmate

+0

@ハンク私はあなたが言及したセクションを読んで、私はあなたが答えが好きでしたが、chibacityは私の質問に私のより適切な答えを与えたので、私はタイマーの設定内でそれを管理し、コールバックfunciton、 – Delashmate

-1

どのようにあなたの共有データについて知ることができましたか?

タイマーコールバックは、いくつかのThreadPoolスレッドで実行されます。したがって、少なくとも2つのスレッドがあります:

  1. タイマーが作成されて起動されるメインスレッド。
  2. コールバックを起動するThreadPoolからのスレッド。

共有データを正しく処理することは、お客様の責任で行ってください。

編集:チベットは完全な例を示しました。

+0

@downvoter:私の答えは何が間違っていますか? –

3

System.Timers.Timerのためにティム・ロイドのソリューションを補完するものとして、ここではあなたの代わりにSystem.Threading.Timerを使用したい場合のために再入を防止するためのソリューションです。

TimeSpan DISABLED_TIME_SPAN = TimeSpan.FromMilliseconds(-1); 

TimeSpan interval = TimeSpan.FromSeconds(1); 
Timer timer = null; // assign null so we can access it inside the lambda 

timer = new Timer(callback: state => 
{ 
    doSomeWork(); 
    try 
    { 
    timer.Change(interval, DISABLED_TIME_SPAN); 
    } 
    catch (ObjectDisposedException timerHasBeenDisposed) 
    { 
    } 
}, state: null, dueTime: interval, period: DISABLED_TIME_SPAN); 

私はあなたがintervalは、コールバックの内側にアクセスすることにしたくないと考えているが、それはあなたがしたい場合は、修正することは容易である:BCLのTimerクラスをラップNonReentrantTimerクラスに上記を入れてください。その後、doSomeWorkコールバックをパラメータとして渡します。そのようなクラスの例:

public class NonReentrantTimer : IDisposable 
{ 
    private readonly TimerCallback _callback; 
    private readonly TimeSpan _period; 
    private readonly Timer _timer; 

    public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) 
    { 
     _callback = callback; 
     _period = period; 
     _timer = new Timer(Callback, state, dueTime, DISABLED_TIME_SPAN); 
    } 

    private void Callback(object state) 
    { 
     _callback(state); 
     try 
     { 
      _timer.Change(_period, DISABLED_TIME_SPAN); 
     } 
     catch (ObjectDisposedException timerHasBeenDisposed) 
     { 
     } 
    } 


    public void Dispose() 
    { 
     _timer.Dispose(); 
    } 
} 
0
/// <summary> 
/// The C#6 version 
/// </summary> 
public class TimerNicer : IDisposable { 
    private Action OnElapsed { get; } 

    [NotNull] 
    private System.Timers.Timer Timer { get; } = new System.Timers.Timer { AutoReset = false, Interval = 1 }; 

    public TimerNicer(Action onElapsed) { 

     this.OnElapsed = onElapsed ?? (() => { 
     }); 

     this.Timer.Elapsed += (sender, args) => { 

      this.Timer.Stop(); // Why not stop the timer here with this? 

      try { 
       this.OnElapsed(); // do stuff here 
      } 
      catch (Exception exception) { 
       Console.WriteLine(exception); 
      } 
      finally { 
       this.Timer.Start(); 
      } 
     }; 

     this.Timer.Start(); 
    } 

    public void Dispose() => this.Timer.Dispose(); 
} 
関連する問題