等待WaitForMultipleObjects

时间:2010-01-10 17:42:11

标签: c++ windows multithreading

我正在尝试为我的FileWatcher课程编写单元测试。

FileWatcher派生自一个Thread类,并使用WaitForMultipleObjects在其线程过程中等待两个句柄:

  1. FindFirstChangeNotification
  2. 返回的句柄
  3. 允许我取消上述等待的事件句柄。
  4. 所以基本上FileWatcher正在等待先发生的事情:文件更改或我告诉它停止观看。

    现在,在尝试编写测试此类的代码时,我需要等待它开始等待。

    Peusdo代码:

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

    问题是存在竞争条件。我需要首先确保FileWatcher已经开始等待(即它的线程现在在WaitForMultipleObjects上被阻止),然后才能触发第2行中的文件更改。我不想使用Sleeps,因为它看起来很hacky并且在调试时一定会给我带来问题。

    我熟悉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 个答案:

答案 0 :(得分:3)

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

编辑:我现在可以看到比赛了。为什么不移动以下行

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

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

答案 1 :(得分:1)

您应该在观察者的构造函数中调用FindFirstChangeNotification()并存储它返回的句柄,以便在您的线程函数中使用。这意味着您将从施工开始时刻开始捕捉变更事件。

一旦你的线程启动它只需要调用两个句柄等待。如果在线程启动之前发生了更改,那么FindFirstChangeNotification()返回的句柄将被发出信号,并且将处理更改。如果您希望线程监视许多更改,那么它应该在处理每个通知后循环并调用FindNextChangeNotification()

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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