Thread exakt 60 mal pro Sekunde durchlaufen lassen

  • C#

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Splamy.

    Thread exakt 60 mal pro Sekunde durchlaufen lassen

    Hallo VB-Community,
    ich würd gerne wissen wie ich am besten einen thread exakt 60 mal pro sekunde durchlaufen lassen, ich hatte schon ein paar Ansätze, aber die meisten gehen auf 63 oder sind bei anderen PCs auf 33, 53 oder sonst was.

    meine Ansätze bisher:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. void GameThread()
    2. {
    3. while (true)
    4. {
    5. Double timeElapsed = 0;
    6. //code
    7. LimitFrameRate(timeElapsed, 60);
    8. }
    9. }
    10. public static void LimitFrameRate(Double timeElapsed, int fps)
    11. {
    12. long waitTime = 1000 / fps; Double timeTaken = absMS() - timeElapsed;
    13. while (timeTaken < waitTime)
    14. {
    15. Thread.Sleep(1);
    16. timeTaken = absMS() - timeElapsed;
    17. }
    18. timeElapsed = absMS();
    19. }
    20. public static Double absMS()
    21. {
    22. DateTime timeBase = new DateTime(2012, 1, 1);
    23. TimeSpan timeDiff = DateTime.Now.ToUniversalTime() - timeBase;
    24. return timeDiff.TotalMilliseconds;
    25. }
    26. }


    Hoffe ihr könnt mir helfen,
    Gruß Splamy

    Edit by nikeee13:
    Falsches Unterforum, da es sich hier um C# handelt. -> Thread verschoben

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

    der kinsi ist ein fuchs xD
    darum den Thread der 60 loops schaffen soll gezielt ausbremsen - mit einer stopwatch bekommste das theoretisch hin indem du misst und berechnest wie lange die Pause (thread.sleep) werden muss. Bedenke aber, dass diese nur auf ganze ms genau misst bzw du den thread nur um ganze ms ausbremsen kannst. Bei 60 loops/s dauert ein loop 16,666ms. eine Abweichung von 0,5ms (durch rundungsfehler) wären also +/- 3% Fehler. Je mehr Loops du anstrebst, desto größer wird der Fehler.
    Wenn es wirklich genau sein soll, kannst du den Performance-Counter in Verbindung mit Busy-Waiting verwenden. Das ginge dann aber ziemlich auf die CPU, ist also nicht unbedingt empfehlenswert.
    Wenn du XNA verwendest, kannst du VSync anmachen.
    Ich weiss aber grad net wie.
    Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.

    BeryJu.org BeryJu.org/Blog
    danke ertsmal für eure antworten,
    die methode mit dem timer hab ich auch schon versucht, ging genauso schlecht:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Sub GameThread()Dim st As New System.Diagnostics.Stopwatch()
    2. st.Start()
    3. While True
    4. Dim timeElapsed As Double = 0
    5. 'code
    6. LimitFrameRate(st, 60)
    7. End While
    8. End Sub
    9. Public Sub LimitFrameRate(ByRef st As System.Diagnostics.Stopwatch, fps As Integer)
    10. Dim waitTime As Long = 1000 \ fps
    11. Dim ste As Double = st.ElapsedMilliseconds
    12. If waitTime > ste Then
    13. Thread.Sleep(CInt(Math.Truncate(waitTime - ste)))
    14. End If
    15. st.Restart()
    16. End Sub
    17. Public Function absMS() As Double
    18. Dim timeBase As New DateTime(2012, 1, 1)
    19. Dim timeDiff As TimeSpan = DateTime.Now.ToUniversalTime() - timeBase
    20. Return timeDiff.TotalMilliseconds
    21. End Function



    mein problem ist dabei, es muss eine feste anzahl an abläufen pro sekunde sein, da dieser thread die geschwindigkeit des spiels kontrolliert. Das Spiel ist in einen Clienten und Server aufgeteilt und der Server schickt jeweils nur die Änderungen (z.B wenn jemand pfeil nach rechts klickt und losläuft). Die anschließende Bewegung soll dann auf allen Clienten genau gleichzeitig ablaufen, da kein datenaustausch mehr stattfindet bis zu nächsten Änderung. ( Zudem wird bei jeder änderung der aktuelle status an den clienten geschickt, d.h. er wird an die soll-stelle gesetzt, aber man sieht dass er dahin sringt da eine differenz bestand. Meist sehr groß...)

    Wenn ihr eine bessere idee für die umsetztng der synchroniesierung habt, würde sich dieses thema erledigen.

    und es ist leider auch net xna, ich verwende irrlicht...

    Gruß Splamy

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

    Prinzipiell ist das ühaupt kein Prob, einen Taktgeber zu basteln, der niemals aus dem Ruder läuft.
    Dazu muß einfach ein Einstiegszeitpunkt gemerkt werden, und daraus kann man sicher errechnen, wieviele Frames schon durchgelaufen sein müssen.
    Und wenn ein Client dann merkt, dasser auch nur minimal zurückgefallen ist, dann kanner die nächsten paar Intervalle ja entsprechend kürzer einstellen, damit er wieder aufholt.
    Also bei sonem selbstregulierenden Taktgeber ist der einzelne Tick ühaupt nicht genauer als bei festeingestellten Taktgebern, aber schon über vlt. 20 Ticks gesehen hängt ein ein selbstregulierter Taktgeber die festeingestellten an Genauigkeit hoffnungslos ab.

    Nur 60/s ist ziemlich hoch getakted, jdfs. für Winforms. Also mein Kenntnisstand ist Jahre alt, und damals konnten die Winforms-Timer kaum kürzere Intervalle bedienen als so 30ms. Vlt. ist heute ja anders, dann gibts ühaupt kein Problem.
    Ansonsten könnte man bei so kurzen Intervallen sich ausdenken, einfach mit Thread.Sleep(nextIntervall) die Taktung von Tick zu Tick jeweils neu einzustellen - kann man ja auch leicht testen, welche Taktrate so möglich ist.

    Splamy schrieb:

    VB.NET-Quellcode

    1. Thread.Sleep(CInt(Math.Truncate(waitTime - ste)))
    Willst Du den Rest Deines Programmes ausbremsen?
    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!

    Splamy schrieb:

    Wenn ihr eine bessere idee für die umsetztng der synchroniesierung habt

    Ich persönlich vermute mal, dass du denselben Fehler machst, wie die meisten ...
    GESCHWINDIGKEIT wird in Wegeinheit pro Zeiteinheit gemessen. Ein BILDWECHSEL ist keine Zeiteinheit. Im Falle eines Computerspiels ist Geschwindigkeit üblicherweise Pixel/Sekunde. Da das universell gültig ist, sind Probleme unwahrscheinlich.
    genau genommen, ist dieser thread auch nicht für die bildwiederholungsrate zuständig ;) die läuft in nem extra thread...
    dieser thread is explizit für die geschwindigkeit.

    @
    RodFromGermany : nunja der vorgang läuft in nem eigenen thread und dieser soll in der tat stehenbleiben bis zum nächsten vorgang

    Die Idee von ErfinderDesRades hat mich auf eine weitere Idee gebracht die ich nun auprobiere.

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

    Splamy schrieb:

    dieser thread is explizit für die geschwindigkeit.

    Wieso braucht man dafür Threads mit exakt definierter Laufzeit (die es unter Windows NIE geben kann!)?

    Ich sags einfach mal klar und klipp: Dein Ansatz/deine "Lösung" ist mit 95%+ Wahrscheinlichkeit falsch/Murks. Statt also am Timing eines Threads zu fummeln, solltest du lieber deinen Ansatz ändern.

    btw:
    Prinzipiell ist das ühaupt kein Prob, einen Taktgeber zu basteln, der niemals aus dem Ruder läuft.

    Ist im Prinzip richtig (was den Taktgeber angeht), aber ansonsten und in Bezug auf dein Problem natürlich völlig falsch. Windows kann JEDERZEIT, JEDEM Thread die Rechenzeit entziehen und einem anderen Thread zuweisen. WIE genau, entscheidet Windows. Wer das nicht möchte, muss auf ein Realtime-OS umsteigen.
    deswegen arbeite ich jetzt auch nicht mit nem timer wie ich mir von vornherein überlegt hab sondern nehm die DateTime Klasse und speichere die millisekunden, damit sollte es eig gehen, da die ja konstant sind

    Habs zum laufen gebracht:
    Ist jetzt ne eigene Klasse, welche im Konstruktor die Framerate bekommt und dann im thread mit limit() aufgerufen wird

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class FrameLimitor
    2. Private FPS As Integer
    3. Private count As Integer
    4. Private timestamp As Long
    5. Public Sub New(_fps As Integer)
    6. FPS = _fps
    7. count = 0
    8. timestamp = 0
    9. End Sub
    10. Public Sub limit()
    11. If getmsec() >= 1000 Then
    12. reset()
    13. Console.Title = count.ToString()
    14. count = 0
    15. End If
    16. If FPS - count <> 0 And (1000 - getmsec()) \ (FPS - count) > 0 Then
    17. Thread.Sleep(CInt((1000 - getmsec()) \ (FPS - count)))
    18. End If
    19. count += 1
    20. End Sub
    21. Private Sub reset()
    22. timestamp = absMS()
    23. End Sub
    24. Private Function getmsec() As Long
    25. Return absMS() - timestamp
    26. End Function
    27. Private Function absMS() As Long
    28. Dim timeBase As New DateTime(2012, 1, 1)
    29. Dim timeDiff As TimeSpan = DateTime.Now.ToUniversalTime() - timeBase
    30. Return CLng(timeDiff.TotalMilliseconds)
    31. End Function
    32. End Class

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

    picoflop schrieb:

    Prinzipiell ist das ühaupt kein Prob, einen Taktgeber zu basteln, der niemals aus dem Ruder läuft.
    Ist im Prinzip richtig (was den Taktgeber angeht), aber ansonsten und in Bezug auf dein Problem natürlich völlig falsch. Windows kann JEDERZEIT, JEDEM Thread die Rechenzeit entziehen und einem anderen Thread zuweisen. WIE genau, entscheidet Windows. Wer das nicht möchte, muss auf ein Realtime-OS umsteigen.
    ich weiß halt nicht genau das Problem, stelle mir vor der GameServer gibt eine "Bewegung" vor, wie "bis Zeitpunkt soundso von SituationAktuell nach SituationNeu verändern".
    Und das könnte man mit selbstkalibrierenden Taktgebern korrekt umsetzen - ist doch schnuppe, wenn die Bewegung in den versch. Clients dabei nie 100% synchron ist - aber auch nie mehr als 1/10s voneinander abweicht.