Benötige speziellen Timer...



  • Guten 'N Abend Allerseits 😉 ,

    Hab grad mal ein kleines Problemchen, wozu mir partout nichts (besseres) einfallen will:

    Ich benötige einen Timer, der über eine Callback-Funktion gesteuert wird (also keine 'Window-Messages'). Die Syntax der Callback-Funktion muss mir es aber erlauben, bestimme Daten an die Callback-Funktion übergeben zu können ('user-data'), da diese dann ver- bzw. bearbeitet werden. Weiterhin darf dieser Timer nicht abhängig von einem Fenster sein (lies: Das Starten des Timers muss ohne Angabe eines Fensterhandles funktionieren). Wichtig ist außerdem, das der Timer nicht in einem seperaten Thread ausgelagert ist!

    Die einzige Lösung die mir dazu bisher einfiel, ist SetTimer mit 'nem Dummy-Window, um dann via GetWindowLong(Ptr)/SetWindowLong(Ptr) und dem Flag GWL_USERDATA meine Daten 'in diesen Fenstern zu verpacken'. Aber diese Lösung ist irgendwie sehr dirty 🤡 .

    Die Standardfunktionen wie SetTimer/KillTimer bzw. timeSetEvent/timeKillEvent funktionieren da ja leider auch nicht 🙄 .

    Ich danke schonmal für Vorschläge;
    Viele Grüße
    CodeFinder :xmas1:


  • Mod

    Verstehe ich nicht. SetTimer kann doch auch mit HWND NULL angegeben werden und dem eine entsprechende TimerProc!



  • Martin Richter schrieb:

    SetTimer kann doch auch mit HWND NULL angegeben werden und dem eine entsprechende TimerProc!

    Jops, ABER^^ wie übergebe ich der TimerProc dann meine Daten ?


  • Mod

    Du setzt den Timer und bekommst eine TimerID. In eine globale Map trägst Du die ID ein und die Daten. Der Timer kann das erste mal sowieso nur aufgerufen werden, wenn die MessageQueue gelesen wird.

    In der TimerProc bekommst Du die ID. Aus der globalen Map holst Du Dir die Daten.

    In etwa (nicht ganz syntaktisch korrekt! Ich habe die Signaturen nicht ganz im Kopf):

    std::map<UINT ,CMyData> g_mapTimerData;
    ...
    
    // Timer setzen
      UINT iTimerId = SetTimer(...,TimerProc);
      g_mapTimer[iTimerId] = myDataForTheTimer;
    
    ...
    // Timerproc
    void TimerProc(UINT uiTimerId,...)
    {
      CMyData &dataForThisTimer = g_mapTimerData[uiTimerId];
      ...
    }
    


  • Hm...ja gut mit globalen Variablen geht das natürlich...aber es gibt keine Möglichkeit darauf zu verzichten ?

    Hintergrund:
    Es geht mir um eine Klasse, die sich um die Wiedergabe von Mediendateien kümmert, intern brauch ich da einen Timer. Da das alles schön gekapselt sein soll, wollte ich auf glob. Var's verzichten. Der Timer ist übrigens ne statische Memberfunktion.

    Ähm, da fällt mir gerade auf:
    Dann kann ich doch eigentlich die std::map auch als statisches Member der Klasse definieren. Das sollte doch gehen, dann hab ich ja Zugriff darauf 🙂 .

    PS: Aber die Idee mit der map ist echt gut! -Danke!-



  • Naja, ist dann eigentlich genauso global, ne? Ob jetzt "static" auf File scope im cpp File versteckt oder "static" in der Klasse ist dann auch schon ziemlich egal...

    Eine andere Möglichkeit wäre noch einen "waitable timer" zu verwenden - guck dir mal SetWaitableTimer an. Dazu muss der Thread halt in einen "alertable state" gehen, damit er den APC ausführen kann ("waitable timer" verwenden nen usermode APC um die Callback Funktion aufzurufen). Dafür hättest du dort dein "userdata" Feld.



  • Hallo Codefinder und Hustbaer,
    Hier ist eine kleine Demo zu "Waitable Timers"

    #define _WIN32_WINNT 0x0500
    
    #include <stdio.h>
    #include <windows.h>
    
    void CALLBACK Timer1Callback (void *param, BOOLEAN timer_or_wait)
    {
        printf (param);
    }
    
    void CALLBACK Timer2Callback (void *param, BOOLEAN timer_or_wait)
    {
        printf (param);
    }
    
    int main()
    {
        HANDLE queue = CreateTimerQueue();
        HANDLE timer1, timer2;
    
        CreateTimerQueueTimer (&timer1, 
                               queue, 
                               Timer1Callback,
                               "Tick Timer #1\n",    // Der Parameter für timer1 
                               0,    // Due time (Starte sofort)
                               1000, // Tick period
                               WT_EXECUTEINTIMERTHREAD);
    
        CreateTimerQueueTimer (&timer2, 
                               queue, 
                               Timer2Callback,
                               "Tick Timer #2\n",    // Der Parameter für timer2 
                               2300, // Starte spaeter
                               1200, // Tick period
                               WT_EXECUTEINTIMERTHREAD);
        Sleep (INFINITE);
    }
    

    🙂



  • Jo das sieht ganz gut aus! Danke erstmal an alle. Werd mir das Morgen mal angucken 😉 .



  • Hi Andromeda, timer-queue timer und waitable-timer sind was unterschiedliches. Du verwendest timer-queue timer, waitable-timer macht man mit den Funktionen:
    CreateWaitableTimer
    SetWaitableTimer
    OpenWaitableTimer
    CancelWaitableTimer



  • Sorry, Hustbaer, ich hatte dein Posting mißverstanden und dachte daß du auch Timerqueues meinst. Ich glaube Timerqueues können die Anforderungen von Codefinder am besten erfüllen.



  • Jo, bei den vielen verschiedenen Timer Implementierungen unter Windows kennt sich sowieso schon keiner mehr aus -- das würfelt man schnell mal durcheinander in der Eile 🙂



  • Hab mich mal n bissl in der MSDN und auf Codeproject.com umgesehen...

    @hustbaer:
    Dein Vorschlag bringt mich auch nicht weiter... denn:

    Codeproject.com schrieb:

    In practice, this means that our main thread will be blocked while we are using waitable timers.

    Quelle: http://www.codeproject.com/system/timers_intro.asp#WaitableTimers

    Das Problem ist ja u.a., dass dabei der Thread, der ein Objekt der Klasse erstellt (und benutzt), nicht geblockt werden darf und das, was regelmäßig innerhalb der spezifizierten Periode passieren soll ebenfalls nicht in einem seperaten Thread ablaufen darf (also muss es auch in dem Thread statt finden, in dem auch das Objekt angelegt wurde).

    @Andromeda:
    Bin mir noch nicht ganz sicher: Arbeiten die Thread-Prozeduren der Timer-Queue's jetzt in einem seperaten Thread, oder wird nur in einem seperaten Thread 'gezählt' (, ob die entsprechende Periode abgelaufen ist) ?

    Ich finde das hier nicht ganz aufschlussreich:

    Codeproject.com schrieb:

    In this case, the wait operation is performed by a thread in the thread pool.

    Quelle: http://www.codeproject.com/system/timers_intro.asp#WaitableTimers
    (ganz unten)

    Was ist denn der Nachteil/Vorteil von Timer-Queue's ?

    Danke schonmal/nochmal für Eure Mithilfe 👍 !

    PS: Hab mir den Artikel noch nicht komplett durchgelesen, da mir zwischenzeitlich ein etwas andere Idee kam (auf die in dem Artikel, wenn ich das richtig überflogen habe, auch später drauf angesprochen wird): Man kann sich seinen Timer ja auch selbst schreiben 🙂 .
    Meine (bisherige) Idee ist folgende:
    Ich erstelle einen Thread in dem einfach die entsprechende (vorher festgelegt) Periode gezählt wird. Anschließend wird die Callback-Funktion aufgerufen, in der die periodisch wiederkehrenden Anweisungen gekapselt sind. Das ganze kann man dann noch in eine Klasse legen (, was glaube ich auch in dem Artikel gemacht wird) 😉 . In dem Thread muss dann natürlich auf eine Abbruchvariable kontrolliert werden, ob man den entsprechenen Timer nicht gerne beendet haben möchte.

    Falls mich einer verstanden hat, was haltet Ihr davon ? :xmas1:



  • Nix halt ich davon, denn genau das kannst du mit timer-queue timern fix und fertig vom System haben. Bzw nicht genau das, aber genau genug 🙂



  • Das heißt also, das ich genau das mit Timer-Queue's erreiche ?
    (Also beim Hauptthread wird NICHT beblocked und die Bearbeitung, also die Callback-Funktion, ist auch kein neuer Thread ?)



  • Das geht doch auch nicht.... Welches Flag soll ich denn da setzen (letzter Parameter ?)

    Siehe: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createtimerqueuetimer.asp

    Das von Andromeda vorgeschlagene Flag WT_EXECUTEINTIMERTHREAD geht auch nicht, denn:

    MSND schrieb:

    The callback function is invoked by the timer thread itself.

    ...

    Habt Ihr meine Fragestellung überhaupt gelesen ? 💡 ➡ 🙄

    Hab so langsam das Gefühl, das man das gar nicht lösen kann :xmas2: .



  • Hallo Codefinder,
    Ja, die Timerqueue-Timer werden in einem separaten Thread ausgeführt. Wenn das absolut nicht sein darf, dann könntest du mit User Mode APCs arbeiten oder dafür ein eigenes Scheduling implementieren. Vielleicht kannst du mit der Windows Fiber-API etwas anfangen?
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/using_fibers.asp
    Grundsätzlich ist jedes Problem unter Windows lösbar. Vielleicht muß man aber manchmal ein paar Einschränkungen in Kauf nehmen.
    🙂



  • N' Abend,

    Also, Ähm, danke aber: 😡 .

    MSDN schrieb:

    Asynchronous Procedure Calls

    An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread, the system issues a software interrupt. The next time the thread is scheduled, it will run the APC function. An APC generated by the system is called a kernel-mode APC. An APC generated by an application is called a user-mode APC. A thread must be in an alertable state to run a user-mode APC.

    Quelle: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/asynchronous_procedure_calls.asp

    Also ich hab doch eigentlich eingangs gesagt (bzw. sogar unterstrichen) was die Rahmenbedingungen sind, oder?

    KEINE THREADS!
    und damit sind auch irgendwelche Synchronisationsmethoden (Fiber-API) hinfällig...

    Andromeda schrieb:

    Grundsätzlich ist jedes Problem unter Windows lösbar.

    Mit so nem Satz wär ich vorsichtig ^^.

    Hm naja trotzdem danke, an alle... . Hab das doch mit nem Dummy-Fenster gemacht. Funktioniert auch soweit, wenns auch ein bissl dirty ist...

    PS: Bin heute sowieso etwas angesäuert...nehmt meine teilweise etwas aggressiv formulierten Antworten nicht zu ernst 🤡 👍 .



  • Hallo,

    CodeFinder schrieb:

    KEINE THREADS!
    und damit sind auch irgendwelche Synchronisationsmethoden (Fiber-API) hinfällig...

    Wenn du Fibers benutzt, werden keine zusätzlichen Threads gestartet. Alle von dir erzeugten Fibers laufen in einem einzigen Thread, das heißt, du must dich dabei nicht mit Threadsynchronisation auseinandersetzen. Dafür mußt du aber die Umschaltung selbst in die Hand nehmen. Vielleicht kennst du das Konzept von Coroutines? Im Prinzip sind Fibers nichts anderes.
    🙂



  • CodeFinder schrieb:

    KEINE THREADS!
    und damit sind auch irgendwelche Synchronisationsmethoden (Fiber-API) hinfällig...

    Wie Andromeda auch schreibt... Fibers laufen i.d.R. in *einem* Thread ab. Für das Scheduling musst Du aber selber sorgen!
    Hab hier ein grosses System mit Fibers laufen! Super Sache! Musst dich nicht mit Snychronisationsproblemen rumärgern, weil es sowas nicht gibt! 😉



  • Hm jo kennsch, aber so läuft das jetzt und das bleibt deswegen jetzt so. Ne komplette Umschaltung zu implentieren lohnt den Aufwand nicht 😉 . Aber thX!

    EDIT, @Jochen:
    Wie lang ist der ('Umschalt-')Code ?^^


Anmelden zum Antworten