Ожидание на WaitForMultipleObjects - PullRequest
2 голосов
/ 10 января 2010

Я пытаюсь написать модульный тест для моего FileWatcher класса.

FileWatcher происходит от класса Thread и использует WaitForMultipleObjects для ожидания двух дескрипторов в своей процедуре потока:

  1. Ручка вернулась с FindFirstChangeNotification
  2. Дескриптор события, который позволяет мне отменить вышеуказанное ожидание.

Так что в основном FileWatcher ждет того, что произойдет раньше: изменение файла или я говорю, чтобы оно прекратило смотреть.

Теперь, когда я пытаюсь написать код, который тестирует этот класс, мне нужно ждать, пока он начнет ждать.

Код Пеусдо:

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

Проблема в том, что есть состояние гонки. Сначала мне нужно убедиться, что FileWatcher начал ждать (т. Е. Его поток теперь заблокирован на WaitForMultipleObjects), прежде чем я смогу инициировать изменение файла в строке # 2. Я не хочу использовать Sleeps, потому что, ну, это кажется хакерским и обязательно вызовет проблемы при отладке.

Я знаком с SignalObjectAndWait, но это на самом деле не решает мою проблему, потому что мне нужно это для "SignalObjectAndWaitOnMultipleObjects" ...

Есть идеи?


Редактировать

Для пояснения, вот упрощенная версия класса FileWatcher:

// 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;
};

Он наследуется от Thread и реализует функцию потока, которая выглядит примерно так (очень упрощенно):

_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;

Обратите внимание, что функция потока ожидает два дескриптора, один - уведомление об изменении, а другой - событие "остановка потока" (унаследованное от Thread).

Теперь код, который тестирует этот класс, выглядит следующим образом:

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();
    }
};

И вызывается из теста CPPUnit:

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

Идея состоит в том, чтобы избавиться от этого сна в середине.

Ответы [ 4 ]

3 голосов
/ 10 января 2010

Нет гонки, вам не нужно ждать, пока FileWatcher введет WaitForMultipleObjects. Если вы выполните изменение до вызова функции, оно просто немедленно вернется.

Редактировать: Теперь я вижу гонку. Почему бы вам не переместить следующую строку

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

из функции потока в конструктор FileChangeWatcher? Таким образом, вы можете быть уверены, что к моменту вызова функции StringToFile файл уже просматривается.

1 голос
/ 13 января 2010

Вы должны вызвать FindFirstChangeNotification() в конструкторе вашего наблюдателя и сохранить дескриптор, который он возвращает для использования в вашей функции потока. Это будет означать, что вы будете ловить события изменений с момента постройки и далее.

Как только ваш поток запущен, он просто вызывает wait для двух дескрипторов. Если изменение произошло до запуска потока, то о дескрипторе, возвращенном FindFirstChangeNotification(), уже будет сообщено, и изменение будет обработано. Если вы хотите, чтобы поток отслеживал множество изменений, он должен выполнить цикл и вызывать FindNextChangeNotification() после обработки каждого уведомления.

0 голосов
/ 12 января 2010

Вызовите CreateEvent (), чтобы создать не сигнализированное событие. Когда поток наблюдателя входит в свой основной цикл (или любой другой), SetEvent (). Между тем, в FileWatcher сначала WaitForSingleObject () для события, а затем, когда это возвращается, WFMO, как вы делали раньше.

0 голосов
/ 10 января 2010

Вместо этого вы могли бы использовать Mutex?Прежде чем поток сможет получить доступ к нужным ресурсам, он должен заблокировать Mutex и разблокировать его для других потоков, которым нужен ресурс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...