[C++] Q: std::condition_variable은 왜 std::unique_lock만 쓰나요?
https://stackoverflow.com/questions/13099660/c11-why-does-stdcondition-variable-use-stdunique-lock
Q: std::condition_variable를 사용할 때 인자로 std::unique_lock만 쓰던데, 이걸 왜 하필 이것만 쓰는지 모르겠네요.
제가 이 문서를 이해한 바에 따르면, std::unique_lock은 기본적으로 lock guard입니다. 두 lock 사이의 상태를 치환하는게 가능하죠.
저는 이제껏 이런데다 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)라는 함수를 사용했는데요. 아마 STL을 POSIX 규격으로 사용하는 것인듯합니다.
근데 이건 mutex를 인자로 받지, lock을 받는건 아니죠.
여기에 무슨 차이가 있는걸까요?
std::condition_variable는 std::unique_lock에만 최적화되어있는건가요??
만약 그렇다면, 정확히 얼마나 더 빠를까요?
A: 기술적인 이유는 없을까요?
전 cmeerw의 답을 추천합니다. 기술적인 이유까지 잘 제시했다고 보기 때문이죠.
한번 달려봅시다.
표준 위원회가 condition_variable에 mutex를 넣기로 결정했다고 칩시다.
그럼 코드는 이러한 형태가 될겁니다.
**void foo() **
{ **
** mut.lock(); **
** //mut가 이 스레드에서 lock을 겁니다.
** ****while (not_ready) **
** cv.wait(mut); **
** **//mut가 이 스레드에서 lock을 겁니다.
** mut.unlock(); **
}
이건 condition_variable를 사용하지 않은 방법이죠.
**/****/mut가 이 스레드에서 lock을 겁니다.로 표시된 **구역에서는 예외 안전성에 대한 문제가 있습니다.
그것도 꽤 심각하죠.
만약 저 공간들이나 cv.wait에서 예외가 던져진다면, try/catch가 예외를 잡아서 잠금을 해제하지 않는 한 mutex의 잠금 상태가 누수(leak)됩니다. 잠금이 풀리지 않아요.
그런데도 이건 프로그래머들에게 요청이 많이 되는 코드 형태입니다.
프로그래머가 예외 안전한 코드를 쓸 줄 알고, unique_lock도 쓸 줄 안다고 칩시다.
이제 코드는 다음과 같이 변합니다.
**void foo() **
{ **
** unique_lock
** ***//mut가 이 스레드에서 lock을 겁니다.
** while (not_ready) **
** cv.wait(lk.mutex
** ****//**mut가 이 스레드에서 lock을 겁니다.
}
아까보단 낫긴 하지만, 여전히 최고의 상황은 아닙니다.
저 condition_variable의 인터페이스는 프로그래머가 작업을 함에 있어 많은 노력을 필요로 합니다.
->condition_variable interface is making the programmer go out of his way to get things to work.
실수로 lk가 mutex를 참조하지 못한다면, 널포인터 역참조가 발생할 수 있습니다.
그리고 condition_variable::wait는 해당 스레드가 mut에 대한 lock을 소유하고 있는지 확인할 방법이 없습니다.
기억해두세요. 프로그래머는 mutex를 노출시키는 잘못된 unique_lock의 멤버함수를 선택할 수도 있습니다.
*lk.release()가 그런 예죠.
그럼 이제 unique_lock
**void foo() **
**{ **
** unique_lock
** ****//**mut가 이 스레드에서 lock을 겁니다.
** while (not_ready) **
** cv.wait(lk); **
** ****//**mut가 이 스레드에서 lock을 겁니다.
}
이 코드는 아주 간단합니다.
예외에 안전하기도 하죠
저기서 wait 함수는 lk.owns_lock()을 체크하고 false라면 예외를 던집니다.
이게 condition_variable의 API 디자인을 유도한 기술적인 이유입니다.
게다가 condition_variable은 lock_guard
하지만 condition_variable::wait가 호출될때, mutex의 lock을 묵시적으로 해제(release)되게 됩니다.
그래서 저 동작은 lock_guard의 사용사례(use case)와 구문(statement)가 부합되지 않습니다.
->So that action is inconsistent with the lock_guard use case / statement.
lock을 함수에서 반환하거나, 컨테이너에 집어넣고서 예외 안전한 방법으로 스코프 기반 패턴이 아닌 mutex들의 잠금을 걸거나 해제하기 위해서는 unique_lock가 필요했습니다. 때문에 unique_lock은 condition_variable::wait에 가장 자연스런 방법입니다.