Timing Probleme unter Win32
-
Hallo,
ich benötige in einer Anwendung exaktes Timing im Milisekundenbereich.
Ich möchte zum Besipiel Pakete auf einen Netwerk Socket im Abstand von exakt 10ms (+-1ms) schicken.
Nun stell ich fest, dass diese Feinheit unter Win32 nur bedingt möglich ist.
Sleep() und co. haben eine deutlich gröbere Auflösung, selbst wenn die Anwendung als REAL_TIME_PRIORITY läuft.
Nun gibt es diese MultimediaTimer, die bis zu ms auflösen können (nach MSDN), aber die an das Vorhandensein von bestimmter Hardware gebunden sind. (An welche?)Kurzum: Ich suche Leute für einen Erfahrungaustausch im Bereich ms-genaues Timing.
Wer hat sowas schonmal umgesetzt und was ist (unter welchen Voraussetzungen) machbar?
Danke,
Scrontch.
-
Es gibt den PerfomanceCounter. In der Doku steht zwar, dass eine bestimmte Hardware benötigt wird, aber eigentlich hat jeder Rechner >486 diese Funktionalität.
Wie genau dieser Timer ist, ist nicht festgelegt. Abfragen kannst du die Genauigkeit mit QueryPerformanceFrequency.
Die Auflösung reicht aber locker bis zu ms; die Frequenz war bei meinem alten Rechner IIRC irgendwas um die 5 MHz.
Ein grundsätzliches Problem bleibt aber bestehen: Windows kann deine Anwendung jederzeit und nach Gutdünken unterbrechen, um anderen Anwendungen den Vorzug zu geben. Dagegen kannst du eigentlich nichts unternehmen, da Windows kein Echtzeitbetriebssystem ist. Mit dem Hochsetzen der Prozesspriorität verringerst du nur die Chance, dass deine Anwendung unterbrochen wird.Warum müssen die Pakete denn genau in 10 ms-Abständen gesendet werden? Wenn die über das Internet gehen, ist der Jitter eh viel höher als 10 oder 1ms. In einem großen LAN kann der Jitter auch schnell 1ms überschreiten.
Vielleicht solltest du die Flusskontrolle anders implementieren.
-
> Es gibt den PerfomanceCounter. In der Doku steht zwar, dass eine bestimmte Hardware benötigt wird, aber eigentlich hat jeder Rechner >486 diese Funktionalität.
> Wie genau dieser Timer ist, ist nicht festgelegt. Abfragen kannst du die Genauigkeit mit QueryPerformanceFrequency.
> Die Auflösung reicht aber locker bis zu ms; die Frequenz war bei meinem alten Rechner IIRC irgendwas um die 5 MHz.Danke sehr soweit.
Ich werd mir PerformanceCounter etc. mal näher ansehen.
Woher weisst du, dass die benötigte Hardware jeder >486 hat?> Ein grundsätzliches Problem bleibt aber bestehen: Windows kann deine Anwendung jederzeit und nach Gutdünken unterbrechen, um anderen Anwendungen den Vorzug zu geben. Dagegen kannst du eigentlich nichts unternehmen, da Windows kein Echtzeitbetriebssystem ist. Mit dem Hochsetzen der Prozesspriorität verringerst du nur die Chance, dass deine Anwendung unterbrochen wird.
Das ist mir bewusst. Kann aber in Kauf genommen werden.
> Warum müssen die Pakete denn genau in 10 ms-Abständen gesendet werden? Wenn die über das Internet gehen, ist der Jitter eh viel höher als 10 oder 1ms. In einem großen LAN kann der Jitter auch schnell 1ms überschreiten.
> Vielleicht solltest du die Flusskontrolle anders implementieren.Umgebung ist ein minimal-LAN, in dem neben dem PC nur noch ein IP-Telefon sitzt (also quasi ideale Netzwerk-Bedingungen). Und letzteres erwartet einen RTP Strom mit 10ms Framing.
Das ganze ist als Testappartur zu verstehen.
Es geht gerade darum, zu sehen wie das IP-Telefon mit künstlich erzeugtem Jitter etc. umgeht.
-
scrontch schrieb:
Woher weisst du, dass die benötigte Hardware jeder >486 hat?
Jetzt hast du mich erwischt.

Sagen wir mal so: Ich habe noch nie einen Rechner gesehen, der diesen Counter nicht hatte. Auch hier im Forum wurde so ein Rechner nie genannt.scrontch schrieb:
Umgebung ist ein minimal-LAN, in dem neben dem PC nur noch ein IP-Telefon sitzt (also quasi ideale Netzwerk-Bedingungen). Und letzteres erwartet einen RTP Strom mit 10ms Framing.
Das ganze ist als Testappartur zu verstehen.
Es geht gerade darum, zu sehen wie das IP-Telefon mit künstlich erzeugtem Jitter etc. umgeht.Achso.

-
Ohne auch nur den blassesten Schimmer zu haben:
Könnte man sich nicht auch nen Timer via SetWaitableTimer/CreateWaitableTimer bauen ? - Falls ja, wie genau wäre der eigentlich dann ?
-
Hab mir nun die Dok zu PerformanceCounter angeguckt.
Sind ja nur die zwei Funktionen QueryPerformanceCounter und QueryPerformanceFrequency.
Bloß wie realisier ich damit nun mein Vorhaben etwas alle 10ms ausführen zu lassen??
Wenn ich einfach in einer Schleife QueryPerformanceCounter aufrufe bis der entsprechende Wert erreicht ist werden ja alle anderen Threads blockiert, bzw es entsteht 100% CPU Last.
Selbst wenn ich ein Sleep(0) dazwischenstreue hilft das nichts, denn der Thread hat ja höhere Priorität als z.b. der User-Interface Thread, so dass letzterer quasi nie zum Zug kommt.
Mit einem Sleep(1) hingegen verlier ich aber scheinbar sofort 15ms+ (Win2000) auf einen Schlag, also auch nicht machbar.@geeky:
Nee, der unterliegt eben nur der groben Genauigkeit von zB. 15ms auf meinem Win2000.
-
Nun hält die Win API aber scheinbar noch was anderes parat: Multimedia Timers.
Hört sich nach dem an, was ich suche.
Hier scheint man aber ebenfalls an das vorhandensein bestimmter Hardware gebunden zu sein.
Jemand Erfahrung damit gemacht oder kennt sich sonst gut aus in der Materie?
-
Du hast Recht, Sleep(0) lässt nur Threads mit gleicher Priorität wie der aufrufende Thread zum Zuge kommen:
Sleep
[...]dwMilliseconds
[...]
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution.Versuch mal SwitchToThread() anstelle Sleep(0). AFAIR verzichtet der aktuelle Thread beim Aufruf dieser Funktion auf den Rest seiner Zeitscheibe und lässt andere Threads laufen, und zwar auch solche mit niedrigerer Priorität.
-
Zu den Multimedia-Timern:
Die müssten in der Tat genau genug sein. Bei timeSetEvent kannst du ein Event angeben, dass nach einer gewissen Zeit signalisiert wird.
Dein Aufruf müsste dann in etwa so aussehen (Pseudocode):HANDLE Event = CreateEvent(...); // Delay, Genauigkeit timeSetEvent(10 , 0 , Event, 0, TIME_CALLBACK_EVENT_SET); WaitForSingleObjekt(Event, 100ms);Vielleicht reicht es auch wenn du die Genauigkeit etwas höher als 0 setzt. Das kostet weniger Rechenleistung.
-
Für den Multimedia-Timer hab ich mal ne Klasse gemacht. Aber ich weiß auch nicht genau, welche Hardware-Voraussetzungen da gegeben sind.
Der Header:
//--------------------------------------------------------------------------- #ifndef CMMTimerH #define CMMTimerH //--------------------------------------------------------------------------- #include <windows.h> #include <mmsystem.h> //--------------------------------------------------------------------------- class CMMTimer; VOID CALLBACK TimerProc(UINT, UINT, DWORD, DWORD, DWORD); typedef void (__stdcall* TimerNotification)(CMMTimer* mmTimer); class CMMTimer { private: int FInterval; bool FEnabled; UINT FId; UINT FMinResolution; UINT FMaxResolution; protected: void Enable(); void Disable(); void GetResolution(); void ErrorMessage(LPCTSTR); public: TimerNotification OnTimer; CMMTimer(); ~CMMTimer(); void SetEnabled(bool); bool IsEnabled(); void SetInterval(int); int GetInterval(); }; //--------------------------------------------------------------------------- #endifDie cpp:
#include "CMMTimer.h" //--------------------------------------------------------------------------- CMMTimer::CMMTimer() { FEnabled = false; FInterval = 1000; FId = 0; } //--------------------------------------------------------------------------- CMMTimer::~CMMTimer() { if(FEnabled) Disable(); } //--------------------------------------------------------------------------- bool CMMTimer::IsEnabled() { return FEnabled; } //--------------------------------------------------------------------------- void CMMTimer::SetEnabled(bool enabled) { if(enabled != FEnabled) { FEnabled = enabled; if(enabled) Enable(); else Disable(); } } //--------------------------------------------------------------------------- void CMMTimer::SetInterval(int interval) { FInterval = interval; } //--------------------------------------------------------------------------- int CMMTimer::GetInterval() { return FInterval; } //--------------------------------------------------------------------------- void CMMTimer::GetResolution() { TIMECAPS tc; timeGetDevCaps(&tc, sizeof(TIMECAPS)); FMinResolution = tc.wPeriodMin; FMaxResolution = tc.wPeriodMax; } //--------------------------------------------------------------------------- void CMMTimer::Enable() { GetResolution(); if(FInterval < (int)FMinResolution || FInterval > (int)FMaxResolution) { if(FInterval < (int)FMinResolution) ErrorMessage("Interval is too small"); if(FInterval > (int)FMaxResolution) ErrorMessage("Interval is too high"); } else { timeBeginPeriod(FMinResolution); FId = timeSetEvent(FInterval, FInterval, TimerProc, (DWORD)this, TIME_PERIODIC); } } //--------------------------------------------------------------------------- void CMMTimer::Disable() { timeKillEvent(FId); timeEndPeriod(FMinResolution); } //--------------------------------------------------------------------------- void CMMTimer::ErrorMessage(LPCTSTR msg) { ::MessageBox(NULL, msg, "ERROR", MB_OK | MB_ICONERROR); } //--------------------------------------------------------------------------- VOID CALLBACK TimerProc(UINT, UINT, DWORD dwUser, DWORD, DWORD) { CMMTimer* mm_timer = (CMMTimer*)dwUser; if(mm_timer) if(mm_timer->OnTimer) mm_timer->OnTimer(mm_timer); // *g* } //--------------------------------------------------------------------------- //---------------------------------------------------------------------------