멀티쓰레드 환경에서 가장 중요한 개념중 하나는 역시 Lock이다. 각 쓰레드에서 공유자원에 접근하기전에 해당 자원에 대한 Lock을 얻고 자원에 접근함으로 자원에 대한 상호베제를 얻을 수 있다.
임계영역과 Lock에 대한 설명은 여기서보다 자세하게 다루었다.
vector 공유자원에 두 쓰레드가 동시에 push_back하는 아래코드 예제를 보자.
vector<int32> v;
void Push() {
for (int32 i = 0; i < 10000; i++) {
v.push_back(i);
}
}
int main() {
std::thread t1(Push);
std::thread t2(Push);
t1.join();
t2.join();
cout << v.size() << endl;
return 0;
}
쓰레드 t1과 t2가 벡터에 만번의 데이터를 push_back하는 코드인데 실행해보면 에러가 발생한다.
발생하는 에러는 vector의 특성 때문에 발생하는 에러인데 vector는 연속된 메모리 블록에 값을 저장하지만 할당받은 영역보다 큰 영역을 요구할 때 다른 블록을 할당받고, 데이터를 옮기고, 기존의 데이터 블록을 할당해제한다.
여기서 기존의 데이터 블록을 할당해제할 때 에러가 발생할 수 있다. 멀티 쓰레드 환경에서, t1이 기존의 데이터 블록을 할당해제하고 그 뒤에 t2가 이미 할당해제된 영역을 다시 할당해제하는 경우에 에러가 발생하는 것이다.
그렇다면, 아래와 같이 충분한 영역의 vector 크기를 예약해놓으면 어떻게 될까?
int main() {
v.reserve(20000);
std::thread t1(Push);
std::thread t2(Push);
t1.join();
t2.join();
cout << v.size() << endl;
return 0;
}
위 코드를 실행하면 에러는 발생하지 않지만 기대하는 값이 나오지 않는다. t1과 t2가 각각 만번의 삽입연산을 했기 때문에 출력값은 20000이어야 하지만 더 적은 값이 나온다.

C++에서는 상호베제를 위해 mutex를 사용할 수 있다. 뮤텍스를 통해 임계영역에 해당하는 코드영역에 상호베제를 보장할 수 있다. lock을 이용해 해당 영역에 들어가기 전에 다른 쓰레드(프로세스)가 영역에 존재하는지를 확인한다. 연산이 끝났다면 unlock을 통해 mutex객체의 통제권을 반환한다.
vector<int32> v;
mutex m;
void Push() {
for (int32 i = 0; i < 10000; i++) {
m.lock();
v.push_back(i);
m.unlock();
}
}
mutex는 재귀기능이 없고 recursive_mutex를 사용해야 한다.lock_guard, unique_lock을 사용할 수 있다. lock_guard는 객체가 소멸할 때 자동으로 unlock을 호출해주며 unique_lock은 lock_guard에 이후에 lock을 걸 수 있도록 해주는 기능을 추가한 객체이다.