Simpler Countdown - alle Timer extrem ungenau

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Enixus.

    Simpler Countdown - alle Timer extrem ungenau

    Auch auf die Gefahr hin ausgelacht zu werden:

    Ich brauch einen Timer, der von 60 auf 0 herunterzählt. Sekündlich wird ein Label dementsprechend aktualisiert und die aktuelle Sekunde angezeigt. Dachte ich mir: Schnappst dir ´nen Timer und gut ist. Simpler gehts ja wohl kaum. Naja- bis mir Aussetzer aufgefallen sind. Die Anzeige sprang mit mal: "...56, 55, 54, 53, 51, 50, 49...". Huch, wo ist denn die 52 geblieben? Habe dann mal die exakte Zeit anzeigen lassen und war sehr negativ überrascht. Ich hab zwar schon öfter gehört, dass die Timer ungebau sein sollen, aber dass sie auf kurze Dauer sogar ruckizucki ´ne ganze Sekunde auslassen!?!

    Anbei sei übrigens angememerkt, dass ich mit VS2010 arbeite und versuche gerade WPF zu lernen. Deswegen fing ich erstmal mit einem simplen Auto-Shutdown-Tool an. Das spielt aber im Grunde keine Rolle, weil alle Timer irgendwie gleich ungenau arbeiten... Hier die Werte der versch Timer und dessen signifikanter Fehler:

    System.Windows.Forms.Timer: 0,722
    System.Windows.Forms.Timer: 1,736
    System.Windows.Forms.Timer: 2,750
    System.Windows.Forms.Timer: 3,765
    System.Windows.Forms.Timer: 4,779
    System.Windows.Forms.Timer: 5,792
    System.Windows.Forms.Timer: 6,807
    System.Windows.Forms.Timer: 7,820
    System.Windows.Forms.Timer: 8,834
    System.Windows.Forms.Timer: 9,847
    System.Windows.Forms.Timer: 10,862
    System.Windows.Forms.Timer: 11,876
    System.Windows.Forms.Timer: 12,889
    System.Windows.Forms.Timer: 13,904
    System.Windows.Forms.Timer: 14,918
    System.Windows.Forms.Timer: 15,932
    System.Windows.Forms.Timer: 16,946
    System.Windows.Forms.Timer: 17,961
    System.Windows.Forms.Timer: 18,975
    System.Windows.Forms.Timer: 19,988
    System.Windows.Forms.Timer: 21,2

    System.Windows.Forms.Timer: 22,17
    System.Windows.Forms.Timer: 23,30
    System.Windows.Forms.Timer: 24,45
    System.Windows.Forms.Timer: 25,58
    System.Windows.Forms.Timer: 26,73
    System.Windows.Forms.Timer: 27,86


    System.Timers.Timer: 0,809
    System.Timers.Timer: 1,823
    System.Timers.Timer: 2,837
    System.Timers.Timer: 3,851
    System.Timers.Timer: 4,865
    System.Timers.Timer: 5,879
    System.Timers.Timer: 6,893
    System.Timers.Timer: 7,907
    System.Timers.Timer: 8,921
    System.Timers.Timer: 9,936
    System.Timers.Timer: 10,950
    System.Timers.Timer: 11,964
    System.Timers.Timer: 12,978
    System.Timers.Timer: 13,991
    System.Timers.Timer: 15,5

    System.Timers.Timer: 16,19
    System.Timers.Timer: 17,33
    System.Timers.Timer: 18,47
    System.Timers.Timer: 19,61


    System.Windows.Threading.DispatcherTimer: 0,824
    System.Windows.Threading.DispatcherTimer: 1,836
    System.Windows.Threading.DispatcherTimer: 2,850
    System.Windows.Threading.DispatcherTimer: 3,896
    System.Windows.Threading.DispatcherTimer: 4,911
    System.Windows.Threading.DispatcherTimer: 5,924
    System.Windows.Threading.DispatcherTimer: 6,938
    System.Windows.Threading.DispatcherTimer: 7,952
    System.Windows.Threading.DispatcherTimer: 8,998
    System.Windows.Threading.DispatcherTimer: 10,11

    System.Windows.Threading.DispatcherTimer: 11,25
    System.Windows.Threading.DispatcherTimer: 12,39
    System.Windows.Threading.DispatcherTimer: 13,53
    System.Windows.Threading.DispatcherTimer: 14,114
    System.Windows.Threading.DispatcherTimer: 15,128
    System.Windows.Threading.DispatcherTimer: 16,142

    Ich habe dann nach einiger Google-Suche einen "Microsecond and Millisecond C# Timer" gefunden. Den könnte ich mir natürlich nach VB übersetzen, aber ist das wirklich notwendig? Ich brauch doch nur nen blöden Counter, der in 600 Sekunden auch wirklich 600 mal tickt... Selbst die Windows-Uhr kann das und ich kann irgendwie nicht ganz glauben, dass diese die ganze Zeit nen eigenen Thread für sich beanspracht...

    Hat da einer eine gescheite Lösung für?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Enixus“ ()

    Simpler geht's nimmer, aber nagut. :)

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim WithEvents _winformTimer As New Timer With {.Interval = 1000, .Enabled = True}
    3. Private Sub _winformTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _winformTimer.Tick
    4. Console.WriteLine("System.Windows.Forms: " & Now.ToString("s,fff"))
    5. End Sub
    6. End Class


    Console-Output:
    System.Windows.Forms: 48,931
    System.Windows.Forms: 49,946
    System.Windows.Forms: 50,961
    System.Windows.Forms: 51,974
    System.Windows.Forms: 52,989
    System.Windows.Forms: 54,001

    System.Windows.Forms: 55,016
    System.Windows.Forms: 56,031
    System.Windows.Forms: 57,044


    Beim System.Timers.Timer und dem System.Windows.Threading.DispatcherTimer ändert sich nicht wirklich was erwähnenswertes am Code. Keiner ist genauer...

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Enixus“ ()

    Welchen Timer nutzt du denn jetzt?
    Auszug aus der MSDN:
    Hinweis:
    System.Threading..::.Timer ist ein einfacher, kompakter Zeitgeber, der Rückrufmethoden verwendet und von Threads des Threadpools unterstützt wird. Er wird nicht für die Verwendung mit Windows Forms empfohlen, da die Rückrufe nicht im Thread der Benutzeroberfläche auftreten. System.Windows.Forms..::.Timer ist für die Verwendung mit Windows Forms besser geeignet. Für serverbasierte Zeitgeberfunktionen können Sie u. U. den System.Timers..::.Timer verwenden, der Ereignisse auslöst und über zusätzliche Features verfügt.
    Hallo auch! :)

    Ich hab alle drei probiert: System.Windows.Forms.Timer, System.Timers.Timer, System.Windows.Threading.DispatcherTimer.
    Immer mit dem gleichen ungenauen Ergebnis. (siehe oben)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Enixus“ ()

    Willst Du ein Event gesendet bekommen oder willst Du nur die genaue Zeit haben?
    ZUm lediglich auslesen kannst Du noch den Multimedia-Timer verwenden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Enixus schrieb:

    Ich hab alle drei probiert:

    Falscher Ansatz halt ...

    Der Timer dient NICHT dazu, die Zeit zu messen, sondern nur dazu, die Messung DURCHZUFÜHREN!

    Timer.Interval = 100 (oder 200)
    ElapsedTime = Zielwert - (Jetzt - Startzeit)
    Wenn ElapsedTime < 0 dann Label 0 und Timer Ende
    Sonst Wenn ElapsedTime <> angezeigte Zeit dann angezeigte Zeit = ElapsedTime

    EDIT, Bsp:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private WithEvents tm As New Timer With {.Enabled = False, .Interval = 100}
    3. Private CountdownValue As Integer = 10
    4. Private Startzeit As DateTime
    5. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    6. Me.Label1.Text = "Press Button To Start"
    7. End Sub
    8. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    9. If tm.Enabled Then Exit Sub
    10. Me.Label1.Text = CountdownValue.ToString
    11. Startzeit = Now
    12. tm.Enabled = True
    13. End Sub
    14. Private Sub tm_Tick(sender As Object, e As System.EventArgs) Handles tm.Tick
    15. Dim elapsed = TimeSpan.FromTicks(Now.Ticks - Startzeit.Ticks).Seconds
    16. If CountdownValue - elapsed <= 0 Then
    17. tm.Enabled = False
    18. Me.Label1.Text = "Press Button To Start"
    19. Debug.Print("Elapsed: {0} ms", TimeSpan.FromTicks(Now.Ticks - Startzeit.Ticks))
    20. Else
    21. If Me.Label1.Text <> (CountdownValue - elapsed).ToString Then
    22. Me.Label1.Text = (CountdownValue - elapsed).ToString
    23. Debug.Print("Elapsed: {0} ms", TimeSpan.FromTicks(Now.Ticks - Startzeit.Ticks))
    24. End If
    25. End If
    26. End Sub
    27. End Class


    BTW2:
    Hier die Werte der versch Timer und dessen signifikanter Fehler:

    Der Fehler ist nicht signifikant, sondern der fehlerhaften Umrechnung geschuldet. 19.9 "gerundet" ist 20. Rundet man 21.4 kommt 21 raus. Die Differenz der GERUNDETEN Werte ist also 1 ... und somit völlig korrekt. Schneidet man natürlich nur die Nachkommastellen ab ... aber wer macht das schon ...

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „picoflop“ ()

    System.Windows.Forms: 52,989
    System.Windows.Forms: 54,001
    12ms zu viel: 1,2% Abweichung - ist das so schlimm?
    ich hatte mir übrigens mal in Winforms einen Timer gebastelt, der hat im Tick-Event sein eigenes Intervall angepasst, wenn er ausserhalb eines bestimmten Toleranzbereiches war.

    Der einzelne Tick ist dadurch nicht genauer, aber auch über "endlos" lange Zeiträume blieb das Teil immer im gegebenen Rahmen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    12ms zu viel: 1,2% Abweichung

    Der Absolute Fehler ist (Zielwert - Istwert), also 12 ms.
    Ein relativer Fehler ist überhaupt nicht definiert. Oder wie würdest Du ihn beschreiben?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    stimmt - ist ja ein absoluter Fehler. Ich hatte gedacht: Intervall 1000ms / 12ms Abweichung = 1,2%.

    Aber ist ja Quatsch, denn die Abweichung ist halt immer vmtl. <30ms, was bei kurzem Intervall einen riesigen relativen Fehler ausmacht, und bei langem Interval minimal erscheint.
    Wenn es nur um eine lückenlose Anzeige geht, würde ich einfach nen globalen Integer runterzählen lassen. Da kann er nix auslassen und die Abweichung dürfte auch nach 100en Ticks zu vernachlässigen sein.

    Fiel Fergnügen

    Vatter
    :thumbsup: Seit 26.Mai 2012 Oppa! :thumbsup:

    ErfinderDesRades schrieb:

    12ms zu viel: 1,2% Abweichung - ist das so schlimm?

    Ja, weil dann spätestens nach 84 Sekunden eine ganze Sekunde übersprungen wird. Wie sieht das denn bei einem Countdown bitteschön aus? "10, 9, 8, 7, 6, 5, 4, 3, 1, 0". lol

    ErfinderDesRades schrieb:

    ich hatte mir übrigens mal in Winforms einen Timer gebastelt, der hat im Tick-Event sein eigenes Intervall angepasst, wenn er ausserhalb eines bestimmten Toleranzbereiches war.

    Das war die beste Idee, die auch alle Probleme löst. Das Intervall einfach sehr klein halten ist ja eigentlich unnötig. Dem Dispatcher eine höhere Prioriät zuzuweisen, brachte nur sehr geringfügigen Erfolg.

    Habe jetzt den System.Threading.Timer genommen. Den kann man zwischendurch einfach ein anderes Intervall verpassen, ohne ihn starten und stoppen zu müssen. Einziger Nachteil: Er läuft nicht im GUI-Thread. Aber das ist ja das kleinste Problem. Hier der funzende WPF-Code, falls jemand Interesse hat:

    VB.NET-Quellcode

    1. Class MainWindow
    2. Dim WithEvents _myTimer As System.Threading.Timer
    3. Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    4. _myTimer = New System.Threading.Timer(AddressOf _myTimer_Tick, System.Windows.Threading.Dispatcher.CurrentDispatcher, 1000 - Now.Millisecond, 1000)
    5. End Sub
    6. Private Sub _myTimer_Tick(ByVal formDisp As Object)
    7. 'Invoken, weil eigentlich ein WPF-Label aktualisiert wird. Nur hier im Beispiel wird der Text über die Konsole ausgegeben.
    8. DirectCast(formDisp, Windows.Threading.Dispatcher).Invoke(Sub() Console.WriteLine("System.Threading.Timer: " & Now.ToString("s,fff")))
    9. _myTimer.Change(1000 - Now.Millisecond, 1000) 'Den Timer etwas korrigieren.
    10. End Sub
    11. End Class


    Console-Output schrieb:

    System.Threading.Timer: 0,010
    System.Threading.Timer: 1,009
    System.Threading.Timer: 2,008
    System.Threading.Timer: 3,010
    System.Threading.Timer: 4,007
    System.Threading.Timer: 5,010
    System.Threading.Timer: 6,000
    System.Threading.Timer: 7,013
    System.Threading.Timer: 8,012
    System.Threading.Timer: 9,012
    System.Threading.Timer: 10,010
    System.Threading.Timer: 11,007


    Vielen Dank noch einmal an alle für die Lösungsvorschläge! :)