2011-06-23 7 views
3

私はタイマーの再アクティブ化をワーカースレッドに保つために '一時停止'ラッチを設定できるTimerの派生クラスを作成しようとしました。ただし、AutoResetがfalseに設定されているときにElapsedイベントが引き続き発生し、Paused変数が設定された後でEnabledアクセサがその基本クラスのEnabledプロパティを変更できないように見えます。Enabledがfalseに設定されていると、なぜSystem.Timer.Timerでイベントが発生するのですか?

ここで実際に起こっている相互作用をさらに理解するために、なぜこれが起こっているのですか。

以下の派生クラスの実装を添付しました。

using System.Timers 
    class PauseableTimer : Timer 
    { 
     public bool Paused; 
     new public bool Enabled 
     { 
     get 
     { 
      return base.Enabled; 
     } 
     set 
     { 
      if (Paused) 
      { 
      if (!value) base.Enabled = false; 
      } 
      else 
      { 
      base.Enabled = value; 
      } 
     } 
     } 
    } 

問題の例を示します。

class Program 
{ 
    private static PauseableTimer _pauseableTimer; 
    private static int _elapsedCount; 
    static void Main(string[] args) 
    { 
     _pauseableTimer = new PauseableTimer(){AutoReset = false,Enabled = false,Paused = false}; 

     _pauseableTimer.Elapsed += pauseableTimer_Elapsed; 
     _pauseableTimer.Interval = 1; 
     _pauseableTimer.Enabled = true; 
     while(_elapsedCount<100) 
     { 
      if (_elapsedCount > 50) _pauseableTimer.Paused = true; 
     } 
    } 

    static void pauseableTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     Console.WriteLine(String.Format("this.Enabled:'{0}',Paused:'{1}',AutoReset:'{2}",_pauseableTimer.Enabled,_pauseableTimer.Paused,_pauseableTimer.AutoReset)); 
     _elapsedCount++; 
     _pauseableTimer.Interval = _pauseableTimer.Interval == 1 ? 2 : 1; //This line breaks it. 
     _pauseableTimer.Enabled = true; 
    } 
} 

答えて

4

Relevant document, System.Timers.Timer.Interval

注 を助け、Elapsedイベントのように、一度上昇させIntervalプロパティを設定し、自動リセットが両方のfalseに設定されており、タイマーが以前に有効になっている願っていますEnabledプロパティがtrueに設定されている場合イベントを発生させずに間隔を設定するには、一時的にAutoResetプロパティをtrueに設定します。また、解雇されるイベントを可能にするイベントハンドラの間、trueにAutoResetを設定する文書化されていない振る舞いがあるためtrueにAutoResetを設定する

推奨される解決策は、問題を解決していません。

解決策は、発生したイベントが再び発生する可能性があるように、さまざまな方法のいずれかを維持できるように、派生オブジェクトを構築するように思われます。

以下は、私が終了した実装です。

public class PauseableTimer : Timer 
{ 
    private bool _paused; 
    public bool Paused 
    { 
     get { return _paused; } 
     set 
     { 
      Interval = _interval; 
      _paused = value; 
     } 
    } 

    new public bool Enabled 
    { 
     get 
     { 
      return base.Enabled; 
     } 
     set 
     { 
      if (Paused) 
      { 
       if (!value) base.Enabled = false; 
      } 
      else 
      { 
       base.Enabled = value; 
      } 
     } 
    } 

    private double _interval; 
    new public double Interval 
    { 
     get { return base.Interval; } 
     set 
     { 
      _interval = value; 
      if (Paused){return;} 
      if (value>0){base.Interval = _interval;} 
     } 
    } 

    public PauseableTimer():base(1){} 

    public PauseableTimer(double interval):base(interval){} 
} 
+0

私が見ている問題はまさに問題です。ボトムライン:インターバルを設定しないでください。タイマーが起動します。 –

4

マルチスレッドではすべてが複雑ですが、私は恐れています。あなたのコードはあなたが望むように動作していると仮定すると、Enabledプロパティをリセットした後に機内イベントが発生する可能性のあるウィンドウがあります。 MSDN docsからのこの見積もりを参照してください。

Elapsedイベント を上げるための信号が常に のThreadPoolスレッドで実行するためにキューイングされます。 Enabledプロパティが falseに設定された後、 のElapsedイベントの が表示されることがあります。 Stop メソッドのコード例では、この競合状態を回避する方法の1つが示されています( )。

+0

私はそれを一時停止しようとした後、1つのイベントが発砲することはありません。私は多くのイベントを発射しています。 1つのイベントが解雇されても問題ありません。私はすべての永遠に発射を続けることを望んでいません。 – Erick

+0

その場合、 'Enabled'をリセットするためのあなたのロジックは間違っているはずです。あなたは 'OnElapsed'イベントを変更してロジックを実行する前にチェックできますか?上記のコードでは、例えば、「一時停止」がどのように真実になるかはわかりません。 –

+0

'Paused'はパブリック変数であり、クライアントオブジェクトによって変更されます。私はデバッガの下でコードを実行し、 'Paused'がtrueに設定されると、アクセサが' Enabled'をtrueに設定することを許可していないことを確認しました。私は、 'Elapsed'イベントが発生する前にデバッガを停止させる方法を知らない。 – Erick

-1

私はあなたのコード再フォーマットします:

// from this 
if (!value) base.Enabled = false; 

// to this 
if (!value) 
    base.Enabled = false; 

だけでなく、それはあなたがキーの行にブレークポイントを入れて、それは別のオプション

+1

C#(または少なくともVisual Studio)のブレークポイントは必ずしも1つの行にバインドされているわけではありません。書式に関係なく、ステートメントにブレークポイントを設定できます。 – Joey

+0

ブレークポイントを設定することはできますが、ifまたは割り当てによって停止するかどうかはわかりません。コードの1行に2つの論理的なステップを置くことは悪い習慣です。 –

+0

中括弧?コードの可読性を向上させるには? –

2

を実行されていますかどうかを確認することができ、より良い読みんでありイベントを抑制する???私は何が起こっているのか説明することはできませんが、下に提示されている理論はあなたが議論したこの小さな問題を回避することができるはずです。スティーブが言いましたように、あなたが設定しようとしている実際に設定されていることを確認している '有効なプロパティ上のウォッチとブレークポイントを配置します。 「 - =」サブスクライブ法(ハンドラ)、必要なときのようしてから再度追加「+ =」

キャッチし、「有効」プロパティをチェックし、削除します。私はこれに取り組むだろうか

'Elapsed'イベントを処理する必要があるときにもう一度それを行います。

私はいくつかの異なるWinFormsプロジェクトでこのスタイルをかなり使用しています。 'Elapsed'イベントをプログラムで処理したくない場合は、特定の条件が満たされたときにチェックを作成して削除し、反対の条件が満たされたときに追加します。

if (paused) // determine pause logic to be true in here 
{ 
    timer.Elapsed -= ... // remove the handling method. 
} 
else 
{ 
    timer.Elapsed += ... // re-add it in again 
} 

上記のコードのロジックは、あなたのコードは、「経過」イベントに「一時停止」フラグが真である間、それが発生し、これまでの時間を無視することができます。私は、上記有効にした場合

+0

これは妥当な方法です。私は、イベントハンドラの正確なリストを維持できるかどうかについていくつか懸念がありますが、解決できる問題のように思えます。私はまだ私が見ている行動をなぜ得ているのか理解したい。なぜ私が現在見ているものが見えないのかの解を見つけることができない場合、私はおそらくあなたの解を使うでしょう。ありがとうございました。 – Erick