2010-01-10 41 views
2

我正在尝试为我的FileWatcher类写一个单元测试。Waiting on WaitForMultipleObjects

FileWatcher从Thread类派生,并使用WaitForMultipleObjects等待它的线程过程中两个手柄:

  1. 手柄从FindFirstChangeNotification
  2. 句柄返回事件,让我取消上述等待。

因此,基本上FileWatcher正在等待先发生的任何事情:文件更改或我告诉它停止观看。

现在,当试图编写测试这个类的代码时,我需要等待它开始等待。

Peusdo代码:

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

的想法是在中路摆脱睡眠。

回答

3

有没有比赛,你不必等待FileWatcher进入WaitForMultipleObjects。如果您在调用函数之前执行更改,它将立即返回。

编辑:我现在可以看到比赛。你为什么不从移动线程函数以下行

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

FileChangeWatcher构造?这样,您可以确定在调用StringToFile函数时,该文件已被监视。

+0

你的意思是我可以在调用Wait之前更改文件,但_after_创建更改通知句柄?但是对于这个类来说并不是这样,它不会将创建更改句柄作为事件公开。或者,也许我误解了你.. – 2010-01-10 18:19:06

+1

但是没有其他办法可以做到这一点。实现这个的标准方法是打开要等待的对象(在您的情况下创建文件观察器并启动它),发出其他对象的信号,然后等待第一个对象。这就是它的做法,你需要改变你的课程设计来适应这个。 – wj32 2010-01-10 18:40:04

+0

你不是在'FileWatcher'的构造函数中调用FindFirstChangeNotification吗?该目录应该从那一刻开始观察。也许你可以发布更多的代码,以便我们知道究竟发生了什么。 – avakar 2010-01-10 18:44:15

0

相反,你可以使用Mutex吗?在一个线程可以访问它所需的资源之前,它必须锁定该互斥锁并为其他需要该资源的线程解锁它。

+0

你的意思是什么资源?你如何建议我在这种情况下使用Mutex? – 2010-01-12 14:46:46

0

调用CreateEvent()创建一个非信号事件。当观察者线程进入其主循环(或其他)时,SetEvent()。同时,在事件的FileWatcher中首先使用WaitForSingleObject(),然后一旦返回,WFMO就像以前一样。

+0

但是这造成了另一种竞争条件。 – 2010-01-12 14:46:06

1

您应该在观察者的构造函数中调用FindFirstChangeNotification(),并存储返回的线程函数的句柄。这意味着您将从施工之初就抓住变化事件。

一旦你的线程已经开始,它只是简单地调用两个句柄的等待。如果在线程启动之前发生更改,那么返回的句柄将被发送,并且更改将被处理。如果您希望线程监视许多更改,那么它应该循环并在处理每个通知后调用FindNextChangeNotification()

相关问题