PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [VC++/MFC]WaitForSingleObject vs. CSingleLock::Lock


Imperator Katarn
2006-02-23, 16:57:37
Hi Leute,

Ich arbeitete kürzlich an folgendem Problem:
Ein Worker-Thread sendet eine Nachricht an den Hauptthread, daß dieser eine bestimmte Aktion ausführen soll, und soll dann warten bis der Hauptthread die Aktion durchgeführt hat. Zuerst habe ich eine Realisierung mittels WaitForSingleObject versucht:

HANDLE hEvent;
CMainWnd cMainWnd;

// im Worker-Thread:
// Event erzeugen und mit non-signaled initialisieren
hEvent = CreateEvent(NULL, TRUE, FALSE, "Action finished");
// Nachricht an Programmfenster (kontrolliert vom Hauptthread) senden,
// daß Aktion ausgeführt werden soll
cMainWnd->PostMessage(/*starte Aktion*/);
// auf Beenden der Aktion warten
WaitForSingleObject(hEvent, INFINITE);

// im Hauptthread:
// Funktion, die durch die Nachricht aufgerufen wird
CMainWnd::DoAction()
{
// mache irgendwas...
// Event auf signaled setzen
SetEvent(hEvent);
}

Wenn im Hauptthread hEvent auf signaled gesetzt wird, sollte im Worker-Thread WaitForSingleObject zurückkehren. Nach einigem Experimentieren stellte ich jedoch fest, daß WaitForSingleObject des öfteren schon vorher zurückkehrte, als hEvent noch auf non-signaled stand!
Versuchsweise rief ich nach CreateEvent nochmal extra ResetEvent(hEvent) auf, um hEvent nur ja sicher auf non-signaled zu haben, was aber auch nichts brachte.

Dann probierte ich eine Alternative aus mit CSingleLock::Lock statt WaitForSingleObject:

CEvent *pEvent; // ersetzt hEvent
CMainWnd cMainWnd;

// im Worker-Thread:
// Event erzeugen und mit non-signaled initialisieren
pEvent = new CEvent(FALSE, TRUE, "Action finished", NULL);
pEvent->ResetEvent();
// Nachricht an Programmfenster (kontrolliert vom Hauptthread) senden,
// daß Aktion ausgeführt werden soll
cMainWnd->PostMessage(/*starte Aktion*/);
// auf Beenden der Aktion warten
CSingleLock cSingleLock(pEvent);
cSingleLock.Lock(); // ersetzt WaitForSingleObject

// im Hauptthread:
// Funktion, die durch die Nachricht aufgerufen wird
CMainWnd::DoAction()
{
// mache irgendwas...
// Event auf signaled setzen
pEvent->SetEvent();
}

und so funktioniert es einwandfrei. CSingleLock::Lock im Worker-Thread wartet stets brav darauf, daß das Ereignis im Hauptthread auf signaled gesetzt wird.

Die Frage, die ich mir stelle, ist nun: warum funktioniert die erstere Variante nicht, die zweite dagegen wohl?
Die zweite Variante nutzt die Klassen CEvent und CSingleLock aus der MFC, diese aber baut doch auf der Win32 API auf, so daß die Annahme naheliegend erscheint, daß CSingleLock::Lock nichts anderes als ein WaitForSingleObject macht und CEvent nichts weiter tut als mit einem Event-Handle zu hantieren.
Die zweite Variante soll daher doch eigentlich genau das gleiche tun wie die erste, oder?

zeckensack
2006-02-23, 18:11:38
Du solltest ein Autoreset-Event benutzen, also den zweiten Parameter von CreateEvent auf false setzen. Dann müsste es eigentlich funktionieren.

Schau dir auch mal SignalObjectAndWait an. Ist oft nützlicher als ein pures SetEvent.

Imperator Katarn
2006-02-24, 09:58:53
Du solltest ein Autoreset-Event benutzen, also den zweiten Parameter von CreateEvent auf false setzen. Dann müsste es eigentlich funktionieren.und warum? Das Autoreset bewirkt doch nur, daß das Event nach dem SetEvent gleich wieder auf non-signaled gesetzt wird.

Schau dir auch mal SignalObjectAndWait an. Ist oft nützlicher als ein pures SetEvent.SignalObjectAndWait arbeitet mit zwei Objekten, eines das auf signaled gesetzt wird und eines auf das gewartet wird. Ich sehe nicht so recht, wie ich das in meinem Fall einsetzen kann.

Ich hab da noch was gefunden, nämlich OpenEvent, das den Zugriff auf ein schon existierendes Event-Objekt öffnet. Kann es sein, daß ich das im Hauptthread aufrufen muß, bevor ich ich SetEvent benutze?