2012-07-11 69 views
0

對於相當長的帖子和大量代碼,事先抱歉。倒數計時器的奇行爲

我的應用程序有一個定時自動保存功能。用戶要求我提供剩餘時間的視覺指標。我做了倒計時定時器一些研究,並最終寫了下面的類:

public class CountDownTimer 
    { 

     private Timer timer; 
     private int remaining; 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Count down ticked delegate. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     /// 
     /// <param name="remaining"> The remaining. </param> 
     /// <param name="maximum">  The maximum. </param> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public delegate void CountDownTickedDelegate(int remaining, int maximum); 

     /// <summary> Event queue for all listeners interested in Ticked event. </summary> 
     public event CountDownTickedDelegate Ticked; 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Count down percent delegate. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     /// 
     /// <param name="percent"> The percent. </param> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public delegate void CountDownPercentDelegate(int percent); 

     /// <summary> Event queue for all listeners interested in Percent events. </summary> 
     public event CountDownPercentDelegate Percent; 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Count down done delegate. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public delegate void CountDownDoneDelegate(); 

     /// <summary> Event queue for all listeners interested in Done events. </summary> 
     public event CountDownDoneDelegate Done; 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Gets or sets the maximum value to count down from </summary> 
     /// 
     /// <value> The maximum value. </value> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public int Maximum 
     { 
      get; 
      set; 
     } 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Gets or sets a value indicating whether the timer is Paused. </summary> 
     /// 
     /// <value> true if paused, false if not. </value> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public bool Paused 
     { 
      get; 
      set; 
     } 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Starts this CountDownTimer. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public void Start() 
     { 
      timer = new Timer { 
       Interval = 1000 
      }; 
      timer.Tick += onTimerTick; 
      timer.Enabled = true; 
      remaining = Maximum; 
      Paused = false; 
     } 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Stops this CountDownTimer. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public void Stop() 
     { 
      if (timer == null) 
      { 
       return; 
      } 

      Paused = true; 
      timer.Enabled = false; 
      timer = null; 
      if (Percent != null) 
      { 
       Percent(100); 
      } 
     } 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Resets and restarts this CountDownTimer. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     public void Reset() 
     { 
      Stop(); 
      Start(); 
     } 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     /// <summary> Handles the timer tick event. </summary> 
     /// 
     /// <remarks> Jon, 18/06/2012. </remarks> 
     /// 
     /// <param name="sender"> Source of the event. </param> 
     /// <param name="e">  Event information to send to registered event handlers. </param> 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     private void onTimerTick(object sender, EventArgs e) 
     { 
      if (remaining > 0) 
      { 
       if (Ticked != null) 
       { 
        Ticked(remaining, Maximum); 
       } 

       if (Percent != null) 
       { 
        int percent = remaining * 100/Maximum; 
        Percent(percent); 
       } 

       if (!Paused) 
       { 
        remaining--; 
       } 
       else 
       { 
        remaining = Maximum; 
       } 
      } 
      else 
      { 
       Stop(); 

       if (Done != null) 
       { 
        Done(); 
       } 
      } 
     } 
    } 

我使用的是定時器和每次「火患」我遞減計數器時間。每個減量都會啓動一個事件,以便我的窗體可以直觀地顯示它。當計數器達到零時,另一個事件啓動自動保存。

還有一些其他位允許自動保存如果用戶手動保存或打開新項目時重新啓動。

它似乎爲我工作。但是,用戶報告計時器運行時間越長,自動保存間隔越短。我將計時器設置爲每秒鐘打勾,我的調查顯示它以兩倍的速度運行。因此,如果計數器設置爲60(秒),那麼它每30次運行一次。我無法複製用戶看到的行爲,但他的日誌肯定顯示運行速度太快。

這與主應用程序在同一線程中 - 這可能是一個問題。迄今爲止,我所有的測試都沒有改變任何東西,除了蜱似乎每三次都會連續兩次發射。

非常感謝您的任何見解。

回答

1

一個問題,我看到的是,如果CountDownTimer.Start()被稱爲兩(甚至多個)次,如果沒有適當的CountDownTimer.Stop()電話,你最終有兩個或您Timer對象更激活的情況下,都調用您的onTimerTick()事件處理器。

這可能會導致您描述的效果,因爲所有正在運行的Timer實例都會分別減少剩餘的迭代次數。

你的調用代碼可能嗎?

編輯:

作爲一種解決辦法,我建議你從Start()中調用Stop()。或者甚至更好,您不會爲每次新的倒計時操作重新創建Timer對象。在構造函數中創建Timer對象並僅處理其屬性。

當您銷售Timer對象時,從timer實例中刪除onTimerTick()事件處理程序也不失爲一個好主意。否則,GC無法收集實例timer,因爲它仍保留對其實例CountDownTimer的引用。

+0

可能是。我現在要檢查一下。 – ScruffyDuck 2012-07-11 11:40:31

+0

你是對的。我調用Start()比Stop()更多次我在Stop()中刪除了onTimerTick處理程序,並將Start()添加到Start()。現在廣告作品。非常感謝 – ScruffyDuck 2012-07-11 12:20:55

0

定時器的經過事件在每個時間間隔後都會繼續觸發。您需要先停止Elapsed事件處理程序方法中的計時器,然後在離開方法時再次啓動該計時器。否則,如果事件處理程序方法中的代碼大約需要1秒,則Elapsed事件將再次提升。例如

private void onTimerTick(object sender, EventArgs e) 
{ 
    try 
    { 
     timer.Stop(); 
     //Do your stuff here 
    } 
    finally { timer.Start(); } 
}