2016-09-16 62 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); 

     } 
    } 
}