2009. 10. 11. 21:58

SignalObjectAndWait 함수

DWORD SignalObjectAndwait(
HANDLE hObjectToSignal,
HANDLE hObjectToWaitOn,
HANDLE dwMilliseconds,
BOOL balertable);

특정 커널 오브젝트를 시그널 상태로 만들어주고, 이와는 또 다른 커널 오브젝트가 시그널 상태가 되기를 대기하는 긴으을 원자적으로 수행한다.

이 함수는 두 가지 이유로 인해 윈도우에 추가되었다.

첫째로, 개발자들은 특정 오브젝트를 시그널 상태로 만들어준 후 다른 오브젝트를 대기하는 식의 코드를 자주 작성하게 되는데, 단일 함수로 이와 같은 작업을 수행하게 되면 수행 시간을 절약해 주는 효과가 있다.

둘째로, SignalObjectAndWait 함수를 이용하면 이 함수를 호출한 스레드가 대기 상태에 있음을 보증할 수 있기 때문에 PulseEvent 와 같은 함수를 사용할 때 유용하게 활용될 수 있다. PulseEvent 는 특정 이벤트를 시그널 상태로 변경하였다가 그 즉시 논시그널 상태로 변경하게 되는데, 어떠한 이벤트를 대기 중인 스레드가 없다면 이벤트가 시그널되었는지를 감지할 수가 없다. 이전에 누군가가 다음과 같은 코드를 작성한 것을 본 적이 있다.

// 작업을 수행한다.
SetEvent(hEventWorkerThreadDone);
WaitForSingleObject(hEventMoreWorkToBeDone, INFINITE);
// 추가 작업을 수행한다.

워커스레드는 작업을 완료 후 SetEvent 를 호출하여 작업을 모두 마쳤음을 다른 스레드에게 알려준다. 다른 스레드는 아마도 다음과 같은 코드를 수행하고 있었을 것이다.

WaitForSingleObject (hEventWorkderThreadDone);
PulseEvent(hEventMoreWorkToBeDone);

워커스레드가 수행하고 있는 코드는 정상 동작하지 않을 수 있는 나쁜 구조다. 워커 스레드가 SetEvent 를 호출하면 다른 스레드가 즉각 깨어나서 PuseEvent 를 호출할 것이라 생각하겠지만 워커 스레드가 SetEvent 를 호출하고 hEventMoreWorkToBeDone 을 매개변수로 WaitForSingleObject 를 호출하기 전에 수행 흐름이 다른 스레드로 변경될 수 있다. 이렇게 되면 PuseEvent 가 ㅁ너저 호출되어 hEventMoreWorkToBeDone 에 대한 시그널 후 논시그널로의 변경작업이 먼저 수행되기 때문에 워커 스레드는 SignalObjectAndWait 함수를 이용하여 아래와 같이 재작성될 수 있다. 이 코드는 시그널 상태로의 변경과 대기를 원자적으로 수행하기 때문에 앞서와 같은 문제를 유발하지 않는다.

// 작업을 수행한다.
SignalObjectAndWait(hEventWorkerThreadDone, hEventMoreWorkToBeDone, INFINITE, FALSE);
// 추가 작업을 수행한다.

이 함수를 호출한 직후 다른 스레드로 제어가 넘어간다 해도 워커 스레드가 hEventMoreWorkToBeDone 이벤트를 대기 중인 것을 100% 보장할 수 있으므로 PulseEvent 호출을 통한 이벤트 상태 변경은 항상 감지될 수 있게 된다.

출처 : 제프리 리처의 Windows via C/C++