Win32 멀티스레드 프로그래밍에서 아쉬운 것

Windows Server 2003까지 아쉬운 거라고 해야 맞겠지만 아직 2008이 내 손에 넘어온 것은 아니므로 일단 토픽은 저렇게;

Win32 API가 pthread 라이브러리에 비해서 가장 부족하다고 – 그리고 아쉽게 생각하는게 – 느껴지는게, conditional variable의 존재다. Event 라는 객체를 제공해주긴 하는데 2% 부족하다. 일단 Event는 두 가지 형태로 생성이 될 수 있다 – manual reset event (signaled -> non-signaled 전환이 수동) 와 auto reset event 2가지.

그리고 signaled state로 변환시키는 함수가 2가지 존재한다.

  • SetEvent() : Signaled로 전환. Manual reset event는 ResetEvent()할 때까지 계속 signaled. Auto-reset이면 1개만 깨어나고 나머지는 대기 상태 유지
  • PulseEvent() : Signaled로 갔다가 바로 non-signaled로 전환. Manual reset event이면 대기 중이던 스레드가 전부 깨어나고, auto reset이면 한 개만 깨어난다

…이게 일반적으로 알려진 설명이다.

여기서 문제인 부분은 하나의 API가 하나의 의미를 갖지 않는다는 것 – pthread conditional variable은 생성/제거/대기/1개만 깨우기/죄다 깨우기 이런 식으로 각 함수가 분리되어있다. 하지만 이쪽은 어떻게 생성했는지에 따라 서로 다른 동작을 한다.

그리고 더 무서운 사실은, Win32 API가 저 의미대로도 동작하지 않을 때가 있다는 것이다.

예를 들어 auto reset event라고 해보자.

  1. 스레드 T1이 이벤트에 대기 시작.
  2. T1이 asynchronous procedure call; APC 로 잠시 이벤트 대기 큐에서 벗어남
  3. APC가 실행되는 동안 PulseEvent()가 대기 중이던 이벤트에 호출됨- 기다리는 스레드가 없으니 깨우지 않고 통과
  4. T1이 APC를 끝내고 다시 대기 – 이벤트를 놓친다.

Manual reset event에서 PulseEvent를 하는 경우에도 비슷한 시나리오가 하나 나온다.

물론 APC가 그렇게 자주 사용되는 방식은 아니고 – 적어도 지금은 그렇다고 생각된다 – Wait-Ex류의 함수들을 쓰지 않는 이상 APC가 호출되지도 않는다. 그렇지만 유저 레벨 동기화 객체도 아니고 커널 객첸데 이러는 건 곤란하다.

그런 의미에서 server 2008에서는 conditional variable이 등장한다. 인터페이스도 pthread와 거의 같다 – 다만 pthread에서 이벤트를 흘리지 않기 위한 도구로 pthread_mutex_t 만 쓰지만 Win32에선 critical section이나 SlimRWLock (역시 서버 2008에서 추가)를 쓸 수 있다. 그러니 제발 빨리 발매 쫌.