簡體   English   中英

當Enabled設置為false時,為什么System.Timer.Timer仍會觸發事件?

[英]Why does System.Timer.Timer still fire events when Enabled is set to false?

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

為什么會發生這種情況或我應該使用什么策略來進一步了解這里實際發生的交互?

我已經附加了下面的派生類的實現。

    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;
    }
}

相關文檔,System.Timers.Timer.Interval

注意如果Enabled和AutoReset都設置為false,並且先前已啟用計時器,則設置Interval屬性會導致Elapsed事件被提升一次,就像Enabled屬性已設置為true一樣。 若要設置間隔而不引發事件,可以暫時將AutoReset屬性設置為true。

AutoReset設置為true的建議解決方案無法解決問題,因為在事件處理程序期間還有一個未記錄的行為,即將AutoReset設置為true,同時允許觸發事件。

解決方案似乎是將派生對象構建到可以保持事件可以再次發生的任何顯然方式的任何方面。

以下是我結束的實現。

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){}
}

我擔心多線程中的一切都比較復雜。 假設您的代碼正常工作,有一個窗口可以在重置Enabled屬性后引發正在進行的事件。 請參閱MSDN文檔中的此引用。

提升Elapsed事件的信號總是排隊等待在ThreadPool線程上執行。 這可能導致在Enabled屬性設置為false后引發Elapsed事件。 Stop方法的代碼示例顯示了解決此競爭條件的一種方法。

另一個選擇是壓制事件??? 我無法解釋發生了什么,但下面提出的理論應該可以讓你避開你所討論的這個小問題。 正如史蒂夫提到的那樣,在啟用的屬性上設置“監視和斷點”,您將嘗試設置並確保它實際上已設置。

我該如何解決這個問題:

捕獲並檢查'Enabled'屬性並在需要時刪除' - ='訂閱方法(處理程序),然后在需要處理'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
}

上面的代碼邏輯將允許您在“Paused”標志為真時,代碼忽略“Elapsed”事件。 我希望以上有所幫助

我會重新格式化你的代碼:

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

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

它不僅讀得更好,還可以在關鍵線上設置斷點,看看它是否正在執行

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM