Windows Fiber API

서버의 특정 부분에서 써보자는 얘기가 나와서 사실 관계랑 rein의 개인적인 견해를 정리.

Windows API에는 *nix 에서 user–level thread라고 부르기도하는 “fiber"에 대한 API가 존재한다. 사실 이 Fiber(섬유)란 단어 자체가 말장난이기도한데1, 크게 보면 다음과 같은 구성이 된다.2

  • 각각이 별도의 메모리(=주소 공간)를 사용하는 프로세스;process
  • 하나의 프로세스 안에서 메모리를 공유하는 스레드;thread들.
  • 역시 하나의 프로세스 안에서 메모리를 공유하지만 스레드가 실행해줘야 돌아가는 fiber. 그리고 OS 커널;kernel의 관점에선 보이지 않는다.3

Fiber는 ::ConverThreadToFiber, ::CreateFiber, ::DeleteFiber, ::SwitchToFiber 등의 API를 통해서 제어되는데, 그 시작, 종료 그리고 **Fiber간의 contex-switching을 사용자가 조절할 수 있다(스케쥴링)**는 점에서 스레드랑 가장 차이가 난다. 물론 스레드 A에서 만든 fiber를 스레드 B에서 동작시켜도 되기 때문에 스케쥴링 할 때 스레드-fiber 소유관계를 확인해야하는 문제는 없다.

Windows 시스템 프로그래밍4 에서 소개하는 fiber의 용도는,

  • 특정 유형의 유저레벨 스레드로 구현한 Unix 응용 프로그램의 포팅5
  • Polling을 담당하는 스레드 대신 fiber를 활용
  • Co–routine의 구현. Lua 같은데서 사용되는 코루틴 비슷한 것을 만들 수 있다. Lua와 유사하게(하지만 제한된 형태로) 다른 코루틴 스택에 데이터를 찔러넣을 수도 있다.
  • 스레드 간의 문맥 전환;context–switching보다 fiber 간의 문맥 전환이 훨씬 싼 작업이다. 문맥 전환이 자주 일어나야 하면 더 쓰기 좋을 수 있다.

정도다. 일단 첫번째 유형이 내재된 목적인 것 같은데6 코루틴이나, 절대로 blocking되면 안되는 서버프로그램의 작업 스레드등에의 blocking-job 처리에는 쓰일 수 있을듯도 하다.7 덤으로 ::TerminateThread()가 아주 예외적인 경우를 제외하고는 쓰지 말아야하는 함수인 것과는 대조적으로 ::DeleteFiber()는 아주 안정적으로 동작할 수 있다. 왜냐하면 특정 fiber가 실행될지 말지는 유저 관점에서 완벽히 알 수 있기 때문에.

장점이 있긴하지만 다음과 같은 문제는 좀 괴롭다.

스케쥴링을 직접해야 한다

커널에서 지원되는 스레드나 성숙된 유저 레벨 스레드 라이브러리가 편한(?) 것은 스케쥴링 문제가 직접 노출되어 있지 않기 때문이다. Fiber는 SwitchToFiber()를 호출해서 직접 실행시켜주기 전에는 돌지 않기 때문에 (thread는 언젠가는 OS가 CPU를 쓰게 해준다), 적절한 시점에 적절한 Fiber를 골라서 동작하게 만들어 줄 수 있다. 그리고 스케쥴링은 절대 쉬운 문제가 아니다. 물론 잘만들면 성능 향상을 기대할 순 있다.

Fiber는 비동기 작업의 대안이 아니다

Fiber로 비동기 작업을 할 수 있긴 하지만(polling이나 fiber가 스케쥴링으로), scalable하진 않다. 비동기 작업 당 1개의 fiber가 필요해서 최대 비동기 작업 수를 알아내거나, 전통적인 비동기 작업 처리 방식들을 섞어써야한다 — 그리고 그럴 바엔 그냥 잘 정의된 멀티스레드 응용을 쓰는게 낫다.

Fiber가 블럭되려면 결국엔 스레드가 블럭된다

Fiber는 OS관점에서 보이는게 아니기 때문에, 서로 다른 fiber가 동기화되려면 결국엔 fiber밑에 있는 스레드들을 동기화 시키는 수 밖에 없다. Mutex 같은 커널 객체에 락을 거는 주체는 thread지 fiber가 아니다.

뭐 이런 이유로 정말 필요한 상황 — 예를 들어 co–routine이 필요하다거나 — 이 아니면 멀티스레드 응용에서 fiber 사용은 권장할만한 일이 아니라고 생각한다.


  1. Thread는 실이고 실은 섬유라는 더 작은 단위가 합쳐져서 나온다)) ↩︎

  2. Fiber를 User–level thread로 보면 Unix–like OS에서도 유사하다. ↩︎

  3. 대조적으로 Win32나 linux에서는 개별 스레드가 커널 관점에서 잘 보이며(?), 스케쥴링의 기본단위가 된다. ↩︎

  4. 7장의 파이버 절에서 요약. J. Hart 저. 류광 번역. 정보문화사에서 발행한 3판이 나와있다. ↩︎

  5. 이 부분은 Windows via C/C++ 12 장에서 설명하는 fiber의 용도이기도 하다. ↩︎

  6. MSDN에 따르면 fiber는 잘 정의된 멀티스레딩 응용에 비해 장점이 없으며, 자기 자신의 스레드를 스케쥴링하게 구현된 응용을 포팅하는데 적당하다고 되어 있다. ↩︎

  7. 예를 들어 다른 서버에 특정 데이터를 요청하고 이게 올 때가지 기다리면서 다른 fiber가 실행되게 하거나, 때때로 스케쥴링되서(물론 유저가 해줘야) 데이터가 왔는지 해당 파이버에서 검사하는 식으로 구현될 수 있다. ↩︎