Benötige speziellen Timer...



  • 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 ?^^



  • CodeFinder schrieb:

    Wie lang ist der ('Umschalt-')Code ?^^

    Kannst selber Debugger 😉 (SwitchToFiber) sind aber ca. 20-30 Assembler-Befehle; und benötigt *kein* Switch in den Kernel!!! Reines User-Mode Scheduling.

    PS: Der aktuelle SQL-Server 2005 verwendet intern Fibers um auf maximale Performance zu kommen! Nur durch die minimale Umschaltzeit lässt sich maximaler Durchsatz erzielen 😉



  • Hm k, aber ich kann recht wenig, naja, sehr wenig 😃 , Assembler... Glaub der Aufwand lohnt da nicht :p . Aber ok es wär ne Idee gewesen 😉 .



  • Klar hab ich die Fragestellung gelesen. Anfangs kam "keine Threads", bloss dann kam das:

    CodeFinder schrieb:

    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.

    hustbaer schrieb:

    denn genau das kannst du mit timer-queue timern fix und fertig vom System haben

    Ich meinte: was auch immer der 2. Thread von dem du schreibst machen soll wenn die Zeit rum ist kannst du auch in der Callback Funktion machen die du bei einem timer-queue Timer hast. Klar jetzt was ich meinte? 🙂

    CodeFinder schrieb:

    Also beim Hauptthread wird NICHT beblocked und die Bearbeitung, also die Callback-Funktion, ist auch kein neuer Thread

    Ok, ohne irgendwelche weitere Dinge die wir vom Hauptthread verlangen können ist das *unmöglich*, denn es gibt keinen Punkt wo wir (bzw. "der Timer") uns einklingen können ("kann").
    Wenn der Hauptthread per Definitionem nen Message-Pump macht, dann kannst du das selbe verwenden wie COM es auch macht um mit STA Apartments zu kommunizieren. Lustigerweise ist das genau das was du ursprünglich angedacht hast. Unsauber finde ich es auch, aber wenn die COM Implementierung von MS selbst es so macht wird es wohl die beste Lösung sein:

    MSDN schrieb:

    Dispatching calls to an STA is more complex, simply because no other threads can enter an existing STA. Unfortunately, when ORPC requests arrive from off-host clients, they are dispatched using threads from the RPC thread cache, which by definition cannot execute in the object’s STA. To enter the STA and dispatch the call to the STA’s thread, the RPC thread uses the PostMessage API function to enqueue a message onto the STA thread’s MSG queue as shown in Figure 5.5. This queue is the same FIFO queue used by the windowing system. This means that to finish dispatching the call, the STA thread must service the queue via some variation on the following code:

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
    DispatchMessage(&msg);
    This code implies that the STA thread has at least one window that can receive messages. When a thread enters a new STA by calling CoInitializeEx, COM creates a new invisible window by calling CreateWindowEx. This window is associated with a COM-registered window class whose WndProc looks for a predefined window message and services the corresponding ORPC request by calling the IRpcStubBuffer::Invoke method on the interface stub. Note that because windows, like STA-based objects, have thread affinity, the WndProc will execute in the apartment of the object.

    Der Artikel heisst übrigens "Standard Marshaling Architecture" falls du selbst weiteres nachlesen willst - und sollte auch mit nem Stück aus dem Text leicht in der MSDN zu finden sein.

    Wenn dir ein Fenster pro Objekt zu viel ist kannst du es ala COM machen und ein Fenster für alle Objekte verwenden, du kannst dann ja Parameter als WPARAM/LPARAM mitgeben (bzw. wenn nötig nen Pointer auf was grösseres).

    ---

    Mich hat das ehrlich gesagt leicht überrascht, da ich dachte Windows hätte schon für interne Zwecke eine Message ID über die man gleich in DispatchMessage ne Callback Funktion aufrufen lassen kann ohne dass erst irgendeine WindowProc oder sonstwas aufgerufen werden muss -- was ja der einfachere Weg wäre.

    p.S.: die wohl einfachere Version (wenn's für dich ausreichend ist und funktioniert) wäre wohl SetTimer mit hWnd == 0 zu verwenden, und als Zeiger auf die Daten dann nIDEvent zu verwenden -- in der Callback Funktion solltest du den selben Wert dann wieder mitbekommen. Zumindest interpretiere ich die Doku so -- ist nicht 100% klar geschrieben für mich.

    Allerdings weiss ich nicht was passiert wenn das Programm in dem dein Code dann läuft zufällig den selben Wert für nIDEvent ebenfalls mit hWnd == 0 verwendet... 🙂

    p.S.: sorry dass es so lange geworden ist, aber ich hoffe es hilft diesmal ein wenig 🙂



  • Erstmal (nochmal) thX @hustbaer!!
    Ich hab mich in meiner Beschreibung etwas falsch ausgedrückt...
    "Anschließend wird die Callback-Funktion aufgerufen, in der die periodisch wiederkehrenden Anweisungen gekapselt sind."
    Das ist natürlich schwachsinn... . Man müsste dann dem Hauptprogrammthread ne Nachricht senden, wo dann die Funktion aufgerufen wird. So würde die Funktion ja aus dem Thread aufgerufen und folglich auch darin ausgeführt - Das will ich ja verhindern - . Ich hatte mir das so auch gedacht, nur nicht so geschrieben 😃 .-Sry-

    Werd's jetzt provisorisch mit nem Dummy-Fenster machen (da das jetzt gerade endlich mal funktioniert ^^).

    Das sieht aber auch interessant aus (wobei es ja vorraussetzt das UINT immer 32Bit ist, was ja i.d.R. der Fall ist):

    hustbaer schrieb:

    p.S.: die wohl einfachere Version (wenn's für dich ausreichend ist und funktioniert) wäre wohl SetTimer mit hWnd == 0 zu verwenden, und als Zeiger auf die Daten dann nIDEvent zu verwenden -- in der Callback Funktion solltest du den selben Wert dann wieder mitbekommen. Zumindest interpretiere ich die Doku so -- ist nicht 100% klar geschrieben für mich.

    Ich werd mal gucken, ob ich das noch umbastle, auf jeden Fall: Danke an alle!



  • Also mein MSDN sagt mir dass nIDEvent ein UINT_PTR ist, also ein unsigned integer wo immer min. gleich gross wie ein Pointer ist. Von daher kein Problem.


Anmelden zum Antworten