Timerproblem Arbeit dauert manchmal länger als Zeitfenster



  • Hallo, ich habe folgendes Problem, ich habe einen Timer mittels Threading.Timer realisiert. Jedoch kann es vorkommen, dass die Arbeit im Timer etwas länger dauert als das Zeitfenster vorgibt.

    Bisher habe ich immer gedacht, dass so lange gewartet wird bis der Timer seine Arbeit erledigt hat. Und dieser dann entsprechend um ein Vielfaches der Zeit wieder ausgelöst wird. Aber System.Threading.Timer erzeugt einen neuen Thread zu der entsprechenden Zeit. Der Timer hat seine arbeit ja noch nicht geschafft. Somit konkurrieren diese Timerthreads um die Ressourcen was ja so nicht gewollt ist. Der Timer schien mir praktikabel, da ich ja einen Parameter übergeben wollte! Der folgende Code soll das Vorgehen mal Demonstrieren:

    System::Void Calltimer()
    {
      TimerCallback^ timerDelegate= gcnew TimerCallback(  this , &Tool:Timer);
      System::Threading::Timer^ timer = gcnew System::Threading::Timer(timerDelegate, (Object^) s , 50 , 500);  
    }
    
    System::Void Tool::Timer(Object^ o)
    {
       Debug::WriteLine("Timer angeworfen vor sleep");
       Sleep(3000);
       Debug::WriteLine("Nach Sleep");
    }
    

    Als Ausgabe erhalte ich dann:
    Timer angeworfen vor sleep
    Timer angeworfen vor sleep
    Timer angeworfen vor sleep
    Timer angeworfen vor sleep
    Timer angeworfen vor sleep
    Timer angeworfen vor sleep --> logisch 6x da aller 500ms
    Nach Sleep --> dann im entsprechendem Wechsel weiter

    Gewünscht ist aber so ein Verhalten:
    Timer angeworfen vor sleep
    mache was... (in dem Beispiel Schlafen)
    Nach Sleep

    Timer angeworfen vor sleep
    mache was... (in dem Beispiel Schlafen)
    Nach Sleep

    Wie kann ich dieses Problem bewältigen, dass kein neuer Thread erzeugt wird?



  • Hallo,

    da muss ich noch mal nachfragen. Warum der Timer? Ist der nötig? Oder wirklich nur um Parameter zu übergeben? Threads kann man auch so Parameter übergeben.

    MfG,

    Probe-Nutzer



  • Ja also der Timer dient zur Messdatenerfassung, der Anwender kann in der Oberfläche die Intervallzeit eintragen. Deswegen der Timer, bspw. kann der Anwender aller 50ms einen Messwert auslesen. Es geht hier eher um kleinere Intervalle zwischen 20ms - mehrere Sekunden.



  • Hallo,

    du könntest ja die Zeit messen, die deine Messwerterfassung dauert (System::Diagnostics::Stopwatch) und die Differenz zum gewünschten Intervall errechnen und am Ende der Funktion der Messwerterfassung nach einem Sleep der errechneten Zeit die neue Messwerterfassung starten.



  • taraneas schrieb:

    Hallo,

    du könntest ja die Zeit messen, die deine Messwerterfassung dauert (System::Diagnostics::Stopwatch) und die Differenz zum gewünschten Intervall errechnen und am Ende der Funktion der Messwerterfassung nach einem Sleep der errechneten Zeit die neue Messwerterfassung starten.

    Hi, meine erste Idee war ja ein Thread laufen zu lassen und über Stopwatch die Differenz zu bestimmen. Aber danach stellte sich heraus, dass Sleep mehr als ungenau ist.

    Eine andere Idee war eine Schleife um Stopwatch zu bauen, jedoch geht die CPU-Auslastung dann auch ordentlich in die Höhe!

    Beim Timer ist die Ungenauigkeit gemäß dem Zeitscheibenmodell von 15ms damit kann ich zum Glück leben. Aber bei Sleep waren teilweise wesentlich größere Schwankungen drin. Deswegen bin ich dann auf den Timer übergeganen. Aber das sich im Nachhinein sowas herausstellt wie oben erwähnt hat mich natürlich etwas schockiert.

    Beim Timers.Timer besteht übrigens das selbe Problem, wie kann es sein, dass jedesmal ein neuer Thread aufgemacht wird. Das entspricht eigentlich nicht meiner Vorstellung eines Timers.



  • Mir kam gerade eine Idee, jedoch kann ich die noch nicht testen. Und zwar könnte man ja um die Anweisung ein If setzen, bei dem eine Variable geprüft wird. Und wenn diese nicht freigegeben wurde, kann der Timerthread dann eben nichts machen. Die Frage ist eben nur, ob dann dieser Thread weiterläuft und erneut aufgerufen wird oder nach dem Durchlauf quasi gekillt wird.

    bool inverwendung = false;
    
    System::Void Calltimer()
    {
      TimerCallback^ timerDelegate= gcnew TimerCallback(  this , &Tool:Timer);
      System::Threading::Timer^ timer = gcnew System::Threading::Timer(timerDelegate, (Object^) s , 50 , 500);  
    }
    
    System::Void Tool::Timer(Object^ o)
    {
       //o wird hier gecasted...
    
       if(!inverwendung)
       {
        inverwendung = true;
    
        Debug::WriteLine("Timer angeworfen vor sleep");
        Sleep(3000);
        Debug::WriteLine("Nach Sleep");
    
        inverwendung = false;
       }
    }
    

    Aber irgendwie finde ich die Lösung insofern diese eine ist auch nicht so sauber. Ich hoffe es meldet sich noch jemand der von Timern wohl mehr versteht als ich.



  • IFAIK wird nur solange beim Timer ein neuer Thread aufgemacht, bis alle aus dem Threadpool belegt sind. Warum immer ein neuer Timer genommen wird liegt, daran, dass der jetztie schon belegt ist und weil es gewünscht ist, dass timerDelegate immer aufgerufen wird. Also muss ein neuer / verfügbarer Thread her.

    Oder:

    System::Void Calltimer()
    {
      TimerCallback^ timerDelegate= gcnew TimerCallback(  this , &Tool:Timer);
      System::Threading::Timer^ timer = gcnew System::Threading::Timer(timerDelegate, timer, 50 , 0);  
    }
    System::Void Tool::Timer(Object^ o)
    {
       ((Timer^) o)->Change(Timeout::Infinite, 0);
       //o wird hier gecasted...
    
       if(!inverwendung)
       {
        inverwendung = true;
    
        Debug::WriteLine("Timer angeworfen vor sleep");
        Sleep(3000);
        Debug::WriteLine("Nach Sleep");
    
        inverwendung = false;
       }
    
       ((Timer^) o)->Change(500, 0);
    
    }
    

    Unterschied: nach beenden und vor wiederaufruf vergehen genau 500 ms.
    Jetzt: Die Zeit zwischen zwei Aufrufen = 500 ms + Dauer eines Aufrufes.



  • Na das klingt ja jetzt logisch für mich! Das mit dem Threadpool habe ich mir schon gedacht, da dieser standardmäßig nur 25 zulässt. Da muss ich mir nur noch Gedanken machen, wie ich Object^ o übergebe. Da s bzw. o ein eigenes Objekt ist und nicht die Timerinstanz.

    Macht es Sinn, da ein Array vom Typ Objekt zu instanziieren und dann darin die beiden unterschiedlichen Typen System::Threading::Timer^ timer und meine eigene Klasse abzulegen. Und dieses Array zu übergeben oder geht das noch eleganter?

    Der folgende Ausdruck kann ja jetzt eigentlich verworfen werden. Da ja in deinem Beispiel eh kein weiterer Timer gestartet wird aufgrund von Timeout::Infinite

    if(!inverwendung)
    {
    }
    

    Jetzt: Die Zeit zwischen zwei Aufrufen = 500 ms + Dauer eines Aufrufes.

    Das lässt sich doch jetzt aber mit der Klasses Stopwatch prima optimieren. Indem man vorher und nachher die Zeit misst. Das vom Anwender gewünschte Delay ist ja bekannt. somit kann man doch die Dauer von dem Intervallwert des Anwenders abziehen. Insofern noch Restzeit zur Verfügung steht.

    Vielen Dank nochmal für die Idee 🙂



  • OMFJC

    worker_thread:
        set priority = high
        while (...)
        {
            wait(wake_event)
            do_stuff()
        }
    
    timer_callback:
        signal(wake_event)
    
    main:
        start_thread(worker_thread)
        start_timer(timer_callback, timeout)
    

Anmelden zum Antworten