2 Stimmen

Warten auf WaitForMultipleObjects

Ich versuche, einen Unit-Test für meine FileWatcher Klasse.

FileWatcher leitet sich von einer Thread-Klasse ab und verwendet WaitForMultipleObjects auf zwei Handles in seiner Thread-Prozedur zu warten:

  1. Der Handle, der von FindFirstChangeNotification
  2. Ein Handle für ein Ereignis, mit dem ich die obige Wartezeit abbrechen kann.

Also grundsätzlich FileWatcher wartet auf das, was zuerst kommt: eine Datei-Änderung oder ich sage ihm, dass er die Überwachung beenden soll.

Wenn ich nun versuche, Code zu schreiben, der diese Klasse testet, muss ich warten, bis sie zu warten beginnt.

Peusdo Code:

FileWatcher.Wait(INFINITE)
ChangeFile()
// Verify that FileWatcher works (with some other event - unimportant...)

Das Problem ist, dass es eine Wettlaufbedingung gibt. Ich muss zuerst sicherstellen, dass FileWatcher angefangen hat zu warten (d.h., dass sein Thread jetzt blockiert ist auf WaitForMultipleObjects ), bevor ich die Dateiänderung in Zeile #2 auslösen kann. Ich möchte Sleeps nicht verwenden, weil es, nun ja, umständlich erscheint und beim Debuggen zu Problemen führen wird.

Ich bin vertraut mit SignalObjectAndWait , aber das löst mein Problem nicht wirklich, denn ich brauche es für "SignalObjectAndWaitOnMultipleObjects"...

Irgendwelche Ideen?


bearbeiten

Um das zu verdeutlichen, hier eine vereinfachte Version der FileWatcher Klasse:

// Inherit from this class, override OnChange, and call Start() to turn on monitoring. 
class FileChangeWatcher : public Utils::Thread
{
public:
    // File must exist before constructing this instance
    FileChangeWatcher(const std::string& filename);
virtual int Run();
    virtual void OnChange() = 0;
};

Sie erbt von Thread und implementiert die Thread-Funktion, die in etwa wie folgt aussieht (stark vereinfacht):

_changeEvent = ::FindFirstChangeNotificationW(wfn.c_str(), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);

HANDLE events[2] = { _changeEvent, m_hStopEvent };

DWORD hWaitDone = WAIT_OBJECT_0;
while (hWaitDone == WAIT_OBJECT_0)
{
    hWaitDone = ::WaitForMultipleObjects(2, events, FALSE, INFINITE);

    if (hWaitDone == WAIT_OBJECT_0)
        OnChange(); 
    else
        return Thread::THREAD_ABORTED;
} 
return THREAD_FINISHED;

Beachten Sie, dass die Thread-Funktion auf zwei Handles wartet, einen - die Änderungsmeldung - und den anderen - das Ereignis "Thread anhalten" (geerbt von Thread).

Der Code, der diese Klasse testet, sieht wie folgt aus:

class TestFileWatcher : public FileChangeWatcher
{
public:
    bool Changed;
    Event evtDone;
    TestFileWatcher(const std::string& fname) : FileChangeWatcher(fname) { Changed = false; }
    virtual void OnChange()
    {
        Changed = true;
        evtDone.Set();
    }
};

Und wird von einem CPPUnit-Test aufgerufen:

std::string tempFile = TempFilePath();
StringToFile("Hello, file", tempFile);
TestFileWatcher tfw(tempFile);
tfw.Start();
::Sleep(100); // Ugly, but we have to wait for monitor to kick in in worker thread
StringToFile("Modify me", tempFile);
tfw.evtDone.Wait(INFINITE);
CPPUNIT_ASSERT(tfw.Changed);

Die Idee ist, den Schlaf in der Mitte loszuwerden.

3voto

avakar Punkte 31197

Es gibt kein Wettrennen, Sie müssen nicht auf die FileWatcher zur Eingabe WaitForMultipleObjects . Wenn Sie die Änderung vornehmen, bevor die Funktion aufgerufen wird, kehrt sie einfach sofort zurück.

Bearbeiten: Ich kann das Rennen jetzt sehen. Warum verschieben Sie nicht die folgende Zeile

_changeEvent = ::FindFirstChangeNotificationW(/*...*/);

von der Thread-Funktion zum Konstruktor von FileChangeWatcher ? Auf diese Weise können Sie sicher sein, dass Sie zu dem Zeitpunkt, zu dem die StringToFile Funktion aufgerufen wird, wird die Datei bereits überwacht.

1voto

Len Holgate Punkte 20590

Sie sollten anrufen FindFirstChangeNotification() im Konstruktor Ihres Watchers und speichern Sie das Handle, das er zur Verwendung in Ihrer Thread-Funktion zurückgibt. Dies bedeutet, dass Sie Änderungsereignisse ab dem Zeitpunkt der Konstruktion abfangen.

Sobald Ihr Thread gestartet ist, ruft er einfach wait für die beiden Handles auf. Wenn eine Änderung eintrat, bevor der Thread gestartet wurde, wird das Handle, das FindFirstChangeNotification() zurückgegeben wird, wird bereits signalisiert, und die Änderung wird verarbeitet. Wenn Sie möchten, dass der Thread viele Änderungen überwacht, sollte er eine Schleife bilden und die FindNextChangeNotification() nach der Bearbeitung jeder Meldung.

0voto

Partial Punkte 8851

Können Sie stattdessen eine Mutex verwenden? Bevor ein Thread auf die gewünschten Ressourcen zugreifen könnte, müsste er den Mutex sperren und für andere Threads, die die Ressource benötigen, entsperren.

0voto

John Dibling Punkte 96619

Rufen Sie CreateEvent() auf, um ein nicht signalisiertes Ereignis zu erzeugen. Wenn der Watcher-Thread in seine Hauptschleife (oder was auch immer) eintritt, SetEvent(). In der Zwischenzeit warten Sie im FileWatcher zunächst mit WaitForSingleObject() auf das Ereignis und dann, sobald dieses zurückkehrt, mit WFMO, wie Sie es zuvor getan haben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X