2010-01-10 6 views
2

FileWatcher 클래스의 단위 테스트를 작성하려고합니다.대기 WaitingForMultipleObjects

FileWatcher는 Thread 클래스에서 파생와 스레드 프로 시저에서 두 개의 손잡이를 기다려야 WaitForMultipleObjects을 사용

  1. 핸들이 저 위의 대기를 취소 할 수 있도록하는 이벤트에 대한 FindFirstChangeNotification
  2. 핸들에서 돌아왔다.

따라서 기본적으로 FileWatcher이 무엇이든 기다리고 있습니다. 파일을 변경하거나 시청을 중지하라고합니다.

이제이 클래스를 테스트하는 코드를 작성하려고 할 때 대기가 시작될 때까지 기다려야합니다.

Peusdo 코드 :

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

문제는 경쟁 조건이 있다는 것입니다. FileWatcher가 라인 # 2에서 파일 변경을 트리거하기 전에 대기 (즉, 해당 스레드가 WaitForMultipleObjects에서 차단됨)를 시작했는지 확인해야합니다. 나는 잠자기를 사용하고 싶지 않습니다. 왜냐하면 해키처럼 보이고 디버깅 할 때 문제가 생길 수 있기 때문입니다.

내가 SignalObjectAndWait 잘 알고 있어요,하지만 난에 "SignalObjectAndWaitOnMultipleObjects을"을 필요로하기 때문에 정말, 내 문제가 해결되지 않는 ...

어떤 아이디어가?

// 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에서 상속 및 스레드 기능을 구현하고, 다음과 같은 :


편집

비트를 명확히하기 위해, 여기에 FileWatcher 클래스의 단순화 된 버전입니다 (매우 단순화 됨) :

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

스레드 함수는 두 개의 핸들, 즉 변경 알림 및 다른 하나 인 "스레드 중지"이벤트 (스레드에서 상속)에서 대기합니다.

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

FileWatcherWaitForMultipleObjects을 입력 할 때까지 기다릴 필요가 없습니다. 함수가 호출되기 전에 변경 작업을 수행하면 즉시 반환됩니다.

편집 : 지금 경기를 볼 수 있습니다.왜 FileChangeWatcher의 생성자 스레드 함수에서 다음 줄

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

를 이동하지 않습니다? 그렇게하면 StringToFile 함수가 호출 될 때까지 파일이 이미 감시되고 있음을 확신 할 수 있습니다.

+0

당신은 Wait를 호출하기 전에 파일을 변경할 수 있지만 변경 알림 핸들을 만들려면 _after_을 의미합니까? 그러나 이벤트로 변경 핸들을 만드는 것을 드러내지 않는이 클래스의 경우에는 그렇지 않습니다. 아니면 내가 너를 오해했을 수도 .. –

+1

하지만 다른 방법은 없습니다. 이것을 구현하는 표준 방법은 기다려야 할 객체를 열고 (당신의 경우 파일 감시자를 생성하고 시작) 다른 객체에게 신호를 보낸 다음 첫 번째 객체를 기다리는 것입니다. 그것이 완료된 방법이며,이를 위해 클래스 디자인을 변경해야합니다. – wj32

+0

'FileWatcher'의 생성자에서 FindFirstChangeNotification을 호출하지 않습니까? 그 순간부터 디렉토리를 감시해야합니다. 아마도 더 많은 코드를 게시하여 정확히 무엇이 진행되고 있는지 알 수 있습니다. – avakar

0

대신 뮤텍스를 사용할 수 있습니까? 쓰레드가 원하는 리소스에 접근하기 전에, 뮤텍스를 잠그고 리소스가 필요한 다른 쓰레드를 위해 풀어야한다.

+0

무슨 리소스가 있습니까? 이 경우 뮤텍스를 어떻게 사용할 것을 제안합니까? –

0

신호없는 이벤트를 만들려면 CreateEvent()를 호출하십시오. 워처 스레드가 주 루프 (또는 무엇이든)로 들어갈 때, SetEvent(). 그 사이에, FileWatcher에서 처음 WaitForSingleObject() 이벤트를 한 번 반환하면 WFMO가 이전과 동일합니다.

+0

하지만 그건 대안 경쟁 조건을 만듭니다. –

1

watcher의 생성자에서 FindFirstChangeNotification()을 호출하고 스레드 함수에서 사용하기 위해 반환하는 핸들을 저장해야합니다. 이것은 당신이 건설 순간부터 변화 사건을 포착한다는 것을 의미합니다.

일단 스레드가 시작되면 두 핸들에서 대기합니다. 스레드가 시작되기 전에 변경이 발생하면 FindFirstChangeNotification()이 리턴 한 핸들이 이미 신호가 보내지고 변경 사항이 처리됩니다. thread가 많은 변경을 감시하고 싶은 경우는, 루프마다, 각 통지의 처리 후에, FindNextChangeNotification()를 호출 할 필요가 있습니다.