2016-09-16 3 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でスケジュールし、タイマーを起動するデルタを計算できます。これは絶対時間からのデルタを計算する際のスキューを補正するはずです。ここでは荒例:

// 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の解像度です)。その後、ティックハンドラで、適切な時間が経過したかどうかをチェックし、そうであれば "本当の"ハンドラを呼び出します。あなたの目標がドリフトを避けることであれば、それが発砲するかどうかのテストは、タイマーを開始してから経過した時間に基づいて行われるべきであり、最後の「ティック」から経過した時間には基づいてはなりません。

+0

あなたの最後の点を教えてください: "あなたの目標がドリフトを避けることであれば、それが発砲するかどうかのテストは、タイマーを開始してから経過した時間に基づいているべきであり、最後の"ダニ "から経過した時間ではありません。 –

+0

バックグラウンドワークのクラスと同じです - ティックに基づいてテストしません - タイマーが開始されてからの実際の時間に基づいたテスト。ループでテストし、時間が近づくにつれてスリープ時間を短縮します。あなたのイベントを起動し、プロセスを開始して、スリープ時間を1000に増やしてください(あなたの繰り返しが約2秒のオーダーであると仮定して); –

+0

+1答えの最も重要な点はWindowsはできません正確な最小期間(マシン/マザーボード)は、ミリ秒レベルで測定したときにスキップ/ドリフトするように見えます。 – 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); 

     } 
    } 
} 
関連する問題