Effective C++/Effective STL
[Effective STL] 12. 쓰레드 안전성
김디트
2024. 11. 19. 10:52
728x90
항목 12. STL 컨테이너 쓰레드 안전성에 대한 기대는 현실에 맞추어 가지자
다중 쓰레딩을 지원하는 STL의 동작
- 여러 쓰레드에서 읽는 것은 안전하다.(Multiple readers are safe)
- 여러 쓰레드에서 다른 컨테이너에 쓰는 것은 안전하다.(Multiple writers to different containers are safe)
- SGI(실리콘 그래픽스사) STL이 이를 지원했다.
- 허나 현재 와서는 개발이 중단되었다..
컨테이너의 완벽한 쓰레드 안전성 구현을 위해 라이브러리에서 해야할 것
- 컨테이너의 멤버 함수를 호출하는 시간 동안에 컨테이너에 락(lock)을 걸기
- 컨테이너가 만들어 내어 주는 반복자의 유효 기간 동안에 컨테이너에 락을 걸기
- 컨테이너에 대해 실행된 알고리즘의 수행 시간 동안에 컨테이너에 락을 걸기
- 알고리즘은 자신이 조작하고 있는 컨테이너를 식별할 방법이 없으므로 사실상 불가능하다.(항목 32 참조)
라이브러리가 쓰레드 안전성을 챙기는 것의 어려움
vector<int> v;
...
// 처음 찾은 5를 0으로 바꾸는 코드
vector<int>::iterator first5(find(v.begin(), v.end(), 5)); // 실행줄 1
if(first5 != v.end()) // 실행줄 2
{
*first5 = 0; // 실행줄 3
}
- 실행줄 1 이후, 다른 쓰레드가 v를 바꿔버릴 수 있다.
- 기존 반복자가 무효화될 것이다.(항목 14 참조)
- 실행줄 2에서 정의되지 않은 실행결과(시스템 다운, 프로그램 다운)가 발생한다.
- 실행줄 3의 대입문 역시 마찬가지로 무효화 시 문제가 발생한다.
- 그렇다면 앞의 세 가지 '락 걸기'로 이 문제를 막을 수 있을까?
- 실행줄 1에 락을 건다면?
- 해당 줄만 쓰레드 안전성을 보장해봤자, 실행 이후에는 동일한 문제가 발생한다.
- 결국 락을 걸려면 실행줄 전부에 락을 걸 수 있어야 한다.
- 헌데 이것을 STL 구현에서 보장해줄 수 있을까?
- 보장한다 쳐도 심각한 수행 성능 저하가 발생할 것이다.
- 실행줄 1에 락을 건다면?
- 즉, 사용자가 쓰레드 동기화를 제어하는 것이 낫다.
vector<int> v;
...
getMutexFor(v);
vector<int>::iterator first5(find(v.begin(), v.end(), 5));
if(first5 != v.end())
{
*first5 = 0;
}
releaseMutexFor(v);
- RAII를 사용하여 좀 더 객체지향적으로 바꾼 버전
// RAII로 간편화
template<typename C>
class Lock
{
public:
Lock(const C& container) : c(container)
{
getMutexFor(c);
}
~Lock()
{
releaseMutexFor(c);
}
private:
const C& c;
};
vector<int> v;
...
{
Lock< vector<int> > lock(v); // 뮤텍스 획득
vector<int>::iterator first5(find(v.begin(), v.end(), 5));
if(first5 != v.end())
{
*first5 = 0;
}
} // 범위를 벗어나며 뮤텍스 해제
- 이 경우 예외에도 안전하다. 예외 발생으로 범위를 벗어난다면 자동으로 해제가 될 것이기 때문이다.
참고
https://github.com/microsoft/STL
GitHub - microsoft/STL: MSVC's implementation of the C++ Standard Library.
MSVC's implementation of the C++ Standard Library. - microsoft/STL
github.com
728x90