2016-09-16 5 views
1

Meine C# -Anwendung muss alle paar Sekunden eine Aufgabe ausführen. Es ist sehr wichtig, dass die Ausführung genau in diesem Intervall stattfindet; gib oder nimm ein paar Millisekunden.Ausführen einer Aktivität in einem bestimmten Intervall

Ich habe versucht, einen Timer zu verwenden, aber die Zeit ändert sich allmählich nach ein paar Minuten. Der Code von mir ist wie folgt:

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

Gibt es einen besseren Weg, diese Art von Aktivität zu tun?

+0

könnten Sie haben mehr Glück mit einem Background Tests für Timelapse, aber es ist immer eine sehr geringe Verzögerung, da alles setzt sein würde. –

+0

Ich habe das gleiche mit der Backgroundworker-Klasse versucht. Aber die Drift ist immer noch da. –

Antwort

1

Sie könnten versuchen, immer einen Singelshot-Timer über AutoReset = false zu planen und das Delta zu berechnen, auf dem der Timer ausgelöst werden soll. Dies sollte Ihre Schräglage kompensieren, wenn das Delta aus der absoluten Zeit berechnet wird. Hier ist ein einfaches Beispiel:

// 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

Ich habe einen Ansatz wie diesen versucht, der das Problem auf jeden Fall gemildert hat, aber im Laufe von ein paar Stunden hat es immer genug gedriftet, um in meinen Daten zu zeigen (1 Sekunde Zeitstempel). – plast1k

3

Wenn es wirklich wichtig ist, genau zu sein, stellen Sie das Intervall Ihres Timers auf etwas kleiner als die maximale Anzahl von Millisekunden ein, um die Sie ausgeschaltet sein können. (Hoffentlich wird dies größer als 15ms sein, da dies die Auflösung von System.Timers.Timer ist.) Überprüfen Sie dann im Tick-Handler, ob die entsprechende Zeit verstrichen ist, und, falls dies der Fall ist, rufen Sie den "echten" Handler auf. Wenn es Ihr Ziel ist, Drift zu vermeiden, sollte Ihr Test, ob es Zeit zum Feuern ist, auf der Zeit basieren, die seit dem Start des Timers verstrichen ist, und nicht auf der Zeit seit dem letzten "Tick".

+0

Könnten Sie bitte Ihren letzten Punkt erläutern: "Wenn Ihr Ziel ist, Drift zu vermeiden, sollte Ihr Test, ob es Zeit zum Feuern ist, auf der seit dem Start des Timers verstrichenen Zeit basieren, nicht auf der Zeit seit dem letzten" Tick ". –

+0

Dasselbe gilt für die Klasse backgroundworker - Sie testen nicht anhand von Ticks - Test basierend auf der tatsächlichen Zeit seit dem Start des Timers Testen Sie in einer Schleife, verringern Sie Ihre Schlafzeit zwischen den Tests, wenn Ihre Zeit sich nähert, so dass es am Ende ist Feuern Sie Ihre Veranstaltung ab und beginnen Sie den Prozess, indem Sie Ihre Ruhezeit auf 1000 erhöhen (vorausgesetzt, Ihre Wiederholung liegt in der Größenordnung von mehreren Sekunden); –

+0

+1 Der wichtigste Punkt in der Antwort ist Windows kann nicht Zeitlich sensible Sachen unter 15ms runterladen haben nicht die exakte minimale Periode (Maschine/Motherboard ist) scheint zu überspringen/driften, gemessen im Millisekundenbereich – 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); 

     } 
    } 
} 
Verwandte Themen