2011-06-23 41 views
3

我試圖創建一個派生類的Timer,它允許設置一個'Pause'閂鎖以防止工作線程重新激活定時器。但是,當AutoReset設置爲false並且Enabled訪問器似乎正在執行阻止在設置Paused變量時修改基類的Enabled屬性的工作時,Elapsed事件繼續發生。當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

注 如果啓用和自動復位都設置爲false,定時器先前已啓用,設置間隔屬性會導致一旦引發Elapsed事件,如如果Enabled屬性已被設置爲true。要設置間隔而不引發事件,可以暫時將AutoReset屬性設置爲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事件 的信號總是排隊上 線程池線程執行。在Enabled屬性設置爲 false之後,可能會導致Elapsed事件中的 被觸發 。 Stop 方法的代碼示例顯示瞭解決此競爭條件的一種方法。

+0

我在嘗試暫停它後沒有收到一個事件觸發。我得到許多事件發射。一個事件被解僱是可以接受的,我只是不希望它繼續爲所有永恆開火。 – Erick

+0

在這種情況下,我認爲你的重置'Enabled'的邏輯必須是錯誤的。你可以在執行邏輯之前修改你的'OnElapsed'事件來檢查它嗎?例如,在上面的代碼中,我看不出'暫停'如何。 –

+0

「已暫停」是一個公共變量,由客戶端對象修改。我已經在調試器下運行代碼,並驗證一旦'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中)不一定綁定到一行。無論格式如何,您都可以在語句上設置斷點。 – Joey

+0

您可以設置一個斷點,但不知道是否由於if或賦值而停止。在一行代碼中放置兩個邏輯步驟是一種不好的做法。 –

+0

大括號?爲了更好的代碼可讀性? –

2

另一種選擇是壓制事件???我無法解釋發生了什麼,但下面提出的理論應該可以讓你繞開你所討論的這個小問題。正如史蒂夫提到的那樣,在你設置的「已啓用屬性」上放置了一個'Watch and break point',並確保它已被設置。

我將如何解決這個:

捕捉並檢查「已啓用」屬性,並刪除「 - =」訂閱方法(處理)在需要時再重新添加「+ =」當你需要處理'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 
} 

上面的代碼邏輯將允許您在代碼忽略'Elapsed'事件時,在'Paused'標誌爲真時引發'Elapsed'事件。希望以上有助於

+0

這似乎是一個合理的方式來做到這一點。我對能夠維護事件處理程序的準確列表有一些擔憂,但這些似乎是可解決的問題。我仍然想明白爲什麼我會看到我所看到的行爲。如果找不到我目前看到的內容的解決方案,我可能會使用您的解決方案。謝謝。 – Erick

相關問題