Forum: PC-Programmierung Windows Timer


von bernd (Gast)


Lesenswert?

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

von geeky (Gast)


Lesenswert?

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...

von bernd (Gast)


Lesenswert?

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

von geeky (Gast)


Lesenswert?

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...)

von geeky (Gast)


Lesenswert?

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...

von Kritiker (Gast)


Lesenswert?

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.

von bernd (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.