2011-09-08 48 views
0

我對有以下行爲計時器的要求:如何正確實現自定義定時在.net

  • 毫秒精度
  • 我想Tick事件處理程序只會被調用一次電流節拍處理已完成(很像的WinForms定時器)
  • 我想在主UI線程不被線程計時器被吞噬的異常,所以這需要調用/發送,而不是的BeginInvoke /後

我玩過CreateTimerQueueTimer,取得了一些成功,但同時在刪除定時器時遇到代碼重入和/或鎖定問題。

我決定創建自己的計時器,以便我可以更好地瞭解引擎蓋下發生了什麼,以便我可以修復鎖定和再入口問題。我的代碼似乎很好地使我相信我可以使用它。它看起來很健康嗎?

我已經放入了一個檢查計時器是否被刪除,以確保刪除完成之前,可以再次創建計時器。這看起來好嗎?

注意:我應該說,我打電話timeBeginPeriod(1)timeEndPeriod(1),以達到毫秒的準確度。

(下面的代碼是從vb.net轉換爲C#,所以對於任何道歉錯過亂七八糟起坐}

ETA:我發現了一個問題與它如果定時器的間隔運行。 1毫秒,和我打電話,說,​​,它鎖定了@而(this.DeleteRequest)。這 一定是因爲TimerLoopthis.CallbackDelegate.Invoke(null)通話。

public class MyTimer : IDisposable 
{ 

    private System.Threading.TimerCallback CallbackDelegate; 
    private bool DeleteRequest; 
    private System.Threading.Thread MainThread; 

    public MyTimer(System.Threading.TimerCallback callBack) 
    { 
     this.CallbackDelegate = callBack; 
    } 


    public void Create(int interval) 
    { 
     while (this.DeleteRequest) { 
      System.Threading.Thread.Sleep(0); 
     } 

     if (this.MainThread != null) { 
      throw new Exception(""); 
     } 

     this.MainThread = new System.Threading.Thread(TimerLoop); 
     // Make sure the thread is automatically killed when the app is closed. 
     this.MainThread.IsBackground = true; 
     this.MainThread.Start(interval); 

    } 

    public void Change(int interval) 
    { 
     // A lock required here? 
     if (!this.IsRunning()) { 
      throw new Exception(""); 
     } 
     this.Delete(); 
     this.Create(interval); 
    } 

    public void Delete() 
    { 
     this.DeleteRequest = true; 
    } 

    public bool IsRunning() 
    { 
     return (this.MainThread != null) && this.MainThread.IsAlive; 
    } 


    private void TimerLoop(object args) 
    { 
     int interval = (int)args; 
     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 


     do { 
      if (this.DeleteRequest) { 
       this.MainThread = null; 
       this.DeleteRequest = false; 
       return; 
      } 

      long t1 = sw.ElapsedMilliseconds; 

      // I want to wait until the operation completes, so I use Invoke. 
      this.CallbackDelegate.Invoke(null); 

      if (this.DeleteRequest) { 
       this.MainThread = null; 
       this.DeleteRequest = false; 
       return; 
      } 

      long t2 = sw.ElapsedMilliseconds; 

      int temp = Convert.ToInt32(Math.Max(interval - (t2 - t1), 0)); 
      sw.Reset(); 
      if (temp > 0) { 
       System.Threading.Thread.Sleep(temp); 
      } 

      sw.Start(); 
     } while (true); 

    } 

     // The dispose method calls this.Delete() 

} 
+1

您可以嘗試發佈此[codereview.se] –

+0

謝謝,我沒有聽說過該網站。 – Jules

+0

「毫秒準確度」是什麼意思?調用睡眠模式將確保您的線程至少在指定的時間內沒有再次安排,但不能保證它的安排時間。 – Joe

回答

1

我會建議使用p/Invoke和使用定時器的Win32的定時器隊列:

http://msdn.microsoft.com/en-us/library/ms686796(v=vs.85).aspx

應當考慮到的是,管理的CLR環境有很多不確定性內置到它,垃圾收集,例如的。僅僅因爲你的計時器有1毫秒的時間並不意味着這一定會發生。

此外,文檔沒有提到它,但定時器調用的回調函數必須通過GCHandle或其他構造固定在內存中,而不是垃圾回收。當一個定時器(或定時器,如果你殺掉一個定時器隊列),回調將被最後一次執行。不確定這是由內部等待過期發生,還是通過發信號通知內部事件句柄。

DeleteTimerQueueTimer()DeleteTimerQueueEx()的執行可以是同步的,所以它們不會返回,直到所有定時器發出信號並調用它們的最後一次回調,但是這樣做會不太理想。

如果你不固定回調並防止它們被垃圾收集,那麼事情就會順利進行......大部分時間。你會遇到隨機異常。

此外,如果定時器被刪除,回調應該足夠聰明,以免引用已經GC'd的東西。