2016-09-16 63 views
1

我的C#應用​​程序必須每隔幾秒執行一次任務。正是在這個時間間隔內執行是非常重要的。給或者幾毫秒。以特定間隔執行活動

我試過使用計時器,但幾分鐘後時間逐漸變化。 由我所使用的代碼如下:

System.Timers.Timer timerObj = new System.Timers.Timer(10 * 1000); 
timerObj.Elapsed += timerObj_Elapsed; 
timerObj.AutoReset = true; 
timerObj.Start(); 

static void timerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
{ 
    DateTime currentTime = DateTime.Now; 
    Console.WriteLine(currentTime.ToString("HH:mm:ss.fff")); 
} 

enter image description here

有沒有更好的方式做這樣的活動?

+0

您可能必須與延時一個BackgroundWorker測試更好的運氣,但總有將是一個非常輕微的滯後。一切復位。 –

+0

我嘗試使用backgroundworker類相同。但漂移仍在。 –

回答

1

您可以嘗試始終通過AutoReset = false安排一個singelshot計時器,並計算計時器應觸發的增量。這應該補償你的偏差,因爲它從絕對時間計算出增量。這裏粗略的例子:

// member variables 
DateTime firstSchedule = DateTime.UtcNow; 
var numElapsed = 1; 

constructor() 
{ 
    this.timerObj = new System.Timers.Timer(); 
    timerObj.Interval = CalcDelta(); 
    timerObj.Elapsed += timerObj_Elapsed; 
    timerObj.AutoReset = false; 
    timerObj.Start(); 
} 

void timerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
{ 
    this.numElapses++; 
    this.timerObj.Interval = CalcDelta(); 
    this.timerObj.Start(); 

    DateTime currentTime = DateTime.Now; 
    Console.WriteLine(currentTime.ToString("HH:mm:ss.fff")); 
} 


private long CalcDelta() 
{ 
    DateTime nextSchedule = firstSchedule + TimeSpan.FromSeconds(numElapses * 10); 
    return (nextSchedule - DateTime.UtcNow).TotalMilliseconds; 
} 
+0

我試過這樣的方法,肯定可以緩解這個問題,但是在幾個小時的過程中,它總是最終漂移到足以顯示我的數據(1秒時間戳)。 – plast1k

3

如果確實非常重要,那麼將您的計時器的間隔設置爲小於您可以關閉的最大毫秒數。 (希望這會大於15ms,因爲這是System.Timers.Timer的分辨率。)然後,在tick處理程序中,檢查是否已經過了適當的時間,如果是,則調用「真實」處理程序。如果您的目標是避免漂移,那麼您是否應該啓動測試的測試應基於自啓動計時器以來的時間,而不是自上次「打勾」以來的時間。

+0

您能解釋一下您的最後一點:「如果您的目標是避免漂移,您是否應該啓動測試應該基於啓動計時器後的時間,而不是自上次」打勾「以來的時間。」 –

+0

與backgroundworker類相同 - 不要基於ticks測試 - 根據自啓動計時器以來實際花費的時間進行測試。循環測試,隨着時間的推移縮短測試之間的睡眠時間,以至於最終每毫秒啓動多次,啓動事件並開始處理,將睡眠時間增加至1000(假設您的重複次數爲幾秒); –

+0

+1答案中最重要的一點是Windows無法做時間敏感的東西下15ms以下。沒有確切的最短時間(機器/主板),似乎會跳過/漂移,當毫秒級別測量時 – kenny

0
private void setTimerRepeat(object sender, DoWorkEventArgs e){ 
     DateTime begin = DateTime.Now; 
     bool isRunning = true; 
     int sleep=500; 
     while(isRunning){ 
      int milliSeconds = DateTime.Now.Subtract(begin).TotalMilliSeconds; 
      if(milliSeconds > 9000){ 
       sleep=10; 
      }else{ 
       sleep=500; 
      } 
      if(milliSeconds=>10000){//if you get drift here, it should be consistent - adjust firing time downward to offset drift (change sleep to a multiple such that sleep%yourNumber==0) 
       begin = DateTime.Now; 
       Task.Run(()=>fireEvent()); 
      } 
      Thread.Sleep(sleep); 

     } 
    } 
}