std::stack에 관한 소고

이하의 정보는 "C++ Concurrency in Action"에서 얻은 것임을 밝힙니다.
(참고로 아직 정식 출간되지 않은 이 책의 맛보기 버전을 여기에 있는 쿠폰코드를 사용하면 싸게 구입하실 수 있습니다.)

remember to thank all the books you haven't read over the past three years

C++ STL에서 제공하는 스택 컨테이너인 std::stack을 아실 겁니다. (정확히는 컨테이너 어댑터입니다만.)

스택하면 push/pop이 기본인데요, 이 std::stack은 실제 스택 꼭대기에 있는 놈을 리턴하는 top()과 실제 그 놈을 스택에서 꺼내는 pop() 함수가 따로 있습니다. 처음 사용할 때, 왜 이렇게 함수를 나눠놨을까 의아해 하였습니다.

이유가 있었더군요. 예외 안정성 때문이었습니다. 그냥 함수 하나로 값 리턴과 꺼내기를 한번에 수행할 경우, 그 리턴값을 지역 변수 등에 복사할 때 예외가 발생하면, 값은 못얻어왔으나 스택에서는 이미 꺼내버려 값이 사라지는 경우가 발생할 수 있습니다. 현 구현처럼 두 함수로 나눠놓으면 top()의 리턴값을 복사하는 와중에 예외가 발생하더라도 아직 꺼내진 않았기 때문에, 자료구조의 상태를 예외 발생 이전대로 안전하게 유지할 수 있는 것이죠.

근데 이러한 선택이 스레드 안전성에서는 오히려 문제가 됩니다. 아래와 같은 race 상황을 근본적으로 막을 수 없기 때문입니다. top()과 pop() 함수 등을 뮤텍스로 잘 보호해놓았어도 여전히 해결이 안되는 인터페이스 자체의 문제라 하겠습니다.


따라서 예외 안정성과 스레드 안정성을 동시에 보장할 수 있는 인터페이스를 고안해야합니다. 여러가지 해결책이 있습니다만, 다음과 같은 두가지 버전의 pop 함수를 제공해 상황에 맞게 사용자가 골라 쓸 수 있게 하는 것을 추천합니다. 위에 것은 포인터로 리턴하므로 복사 시 예외 문제가 없고, 아래 것은 출력 인자에 값을 복사한 후 pop을 하니 역시 예외 안정성이 유지됩니다.
(여러 해법들의 장단점에 대한 구체적 설명은 서두에 언급한 책을 참고해주세요.)


이 경우 빈 스택에 pop을 할 경우 예외를 던집니다. 스택이 비어 있지 않을 때가지 대기하다가 pop하는 등의 좀더 고급 동작을 원하는 경우, condition variable 같은 고급 동기화 개체를 사용해야 합니다. (위 쿠폰코드가 있는 슬라이드에 그 예가 나와있습니다.)

이렇게 생각할게 많은 병렬 프로그래밍이기에, C++0x스레드 라이브러리 지원이 개발자들에게는 천금과 같은 소식이겠습니다. ^^
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 1 Comment 0

top