Hi, wie kann ich folgendes Problem lösen? Ausgangssituation: Sprache C++, System: Windows API (keine MFC, kein .NET). Ich möchte eine Funktion eines Objektes im Millisekundentakt aufrufen. Bisher habe ich immer SetTimer, KillTimer, OnWMTimer verwendet, aber die Klasse ist fensterlos und ich möchte sie aus Portabilitätsgründen nicht an das Hauptfenster ketten. Wie bringe ich also Windows dazu eine static Funktion im Millisekundentakt anzuspringen, ohne dass ich ein Fensterhandel brauche. Ich brauche aber ein 32Bit Referenzwert, damit die statische Funktion beim Aufruf das Objekt im Speicher auch wiederfindet. SetTimer ohne Fensterhandle gibt keinen Parameter beim Aufruf zurück. Auch ist der Timer relativ ungenau. Gibt es da noch einen anderen Timer, der unabhängig von der MessageQueeu ist? Danke, Bernd
Falls in deinem Fall möglich, würde ich folgendes probieren: Nen Thread erzeugen, im Thread einen Waitable Timer benutzen um 1 ms abzuwarten. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/using_waitable_timer_objects.asp (Bei dieser Methode wird keine 100%-CPU-Auslastung erzeugt) und dann deine Aktion ausführen. Evtl. noch aufpassen wielange deine Funktion braucht und das beim setzen des Waitable Timers berücksichtigen, wenn du genau alle 1 ms die funktion ausführen musst und nicht nur im abstand von 1ms. SetTimer() ist dank Nutzung der Windows-Messages vermutlich der ungenaueste Timer den Windows zu bieten hat ;D Recht genau sind die Multimedia Timer und ansonstene evtl. mal die msdn nach QueryPerformanceCounter() durchsuchen...
Man sucht ja immer nach der einfachsten Lösung :-) Im Endeffekt will ich Daten an ein USB Device senden. Das geht Blockweise recht zügig, aber wenn man die Sendefunktion immer nur mit einem Byte aufruft, läuft das ganze zu langsam. Idee ist nun die einzelen Bytes zu sammeln, und dann als 128'er oder 256'er Packete zu schicken. Nur weiß die Funktion ja nicht, wann keine Daten mehr kommen. Also muss ich, wenn zulange keine Daten mehr kommen die Funktion irgendwie antriggern, den Buffer zu leeren. Im Endeffekt reichen mir 1-3ms aus. Wichtig ist nur, dass diese Zeiten auch eingehalten werden. Ich habe die anderen Timer auch schon entdeckt, aber es schein immer darauf hinauszulaufen, dass man einen eigenen Thread für den Timer erzeugt. Dann ist man aber auch gezwungen die Threads zu synchronisieren, womit ich bisher keine Erfahrung habe. Immerhin soll die Senderoutine ja die Daten nicht nur alle 1ms schicken, sondern auch sofort, wenn der Buffer ausreichend gefüllt ist. Es gibt also zwei Trigger, die Zeit und die Aufrufe aus dem laufenden Program. Wie es aussieht, werde ich mir andere Trigger basteln, um häufig genung die Senderoutine anzutriggern. Das Timing muss ja nicht exakt sein. Danke, Bernd
Ich stelle mir das mal stark abstrahiert so vor: Klasse "USBCLient" private int DataToSend=0; private byte[] SendBuffer; private void SendData() { Lock(); // <- Hier sicherstellen das nur ein Thread gleichzeitig diese Funktion betritt, ein Thread darf, die anderen müssten dann erstmal auf Unlock() warten ;D // Hier Daten zum USB-Device kloppen ;D0 Unlock(); // <-- Nu diese Funktion wieder freigeben. } public void FillBuffer(byte[] BytesToSend) { Lock(); // Überall wo wir auf SendBuffer und DataToSend zugreifen sollten wir besser vorher locken damit es dank gleichzeitigem Zugriff nich crashed ;D // SendBuffer und DataToSend entsprechend ändern Unlock(); } public void Init() { // Hier den Timer-Thread starten } public void Cleanup() { // Timer-Thread stoppen } private void DiesWäreDerTimerThread() { CreateWaitableTimer(); // Timer erzeugen SetWaitableTimer(); // Timer auf 1 ms stellen WaitForSingleObject(); // Warten bis 1 ms rum ist // Der Thread triggert: SendData(); } private void public void SofortSendenTrigger { // Die Anwendung triggert: SendData(); } Lock() und Unlock() wären quasi für die Thread-Syncronisation verantwortlich. (Würde bei WinAPI dann EnterCriticalSection() btw. LeaveCriticalSection() bedeuten oder nen Mutex verwenden...)
Hmm, wenn die Anwendung gerade getriggert hat sollte man evtl. den Timer erstmal wieder neustarten, sonst hat der buffer keine zeit sich wieder zu füllen...
Die Windows-Multimediatimer bieten die Möglichkeit, eine Funktion zyklisch aufzurufen. Mit timeBeginPeriod musst Du übrigens zunächst die Timerauflösung des OS erhöhen, normalerweise liegt die bei 10 msec, kann aber auf 1 msec verändert werden. Das zyklische Aufrufen einer Callbackfunktion wird mit timeSetEvent eingerichtet (http://msdn.microsoft.com/library/en-us/multimed/htm/_win32_timesetevent.asp?frame=true) Der Parameter fuEvent muss eine Verorderung der Konstanten TIME_PERIODIC und TIME_CALLBACK_FUNCTION sein, und als Parameter lpTimeProc ist die Adresse der aufzurufenden Funktion zu übergeben. Diese Funktion muss folgenden Prototypen haben: void (CALLBACK)(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); dwUser wird von timeSetEvent direkt an die Rückruffunktion durchgereicht und kann also als der angesprochene Pointer auf eigene Strukturen verwendet werden (this-Pointer für statische memberfunktion o.ä.) Mit timeKillEvent kann die ganze Chose wieder beendet werden. Das ist wesentlich genauer als mit WaitForSingleObject und Konsorten auf irgendwelche Events zu warten. Allerdings ist mehr als 1 msec Auflösung auch auf diese Art und Weise nicht drin. Höhere Auflösung (kürzere Wartezeiten) ist nur noch mit Devicetreibern auf Kernelebene realisierbar. Wenn ich das bislang geschriebene richtig verstehe, dann soll hier einmal pro Millisekunde mit einem USB-Gerät kommuniziert werden - das halte ich für einen völlig verfehlten und zum Scheitern verurteilten Ansatz. USB-Geräte selber werden eh nur im Millisekundentakt vom Stack des USB-Hostes angesprochen, auf Anwendungsebene zu versuchen, mit dieser Granularität Daten loszuwerden, scheint mir aussichtslos.
Danke für die Hilfe. Habe das ganze jetzt ohne Timer realisiert. Ich rufe die Funktion einfach öfter auf. Das funktioniert ganz gut. Danke, Bernd
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.