std::mutex
cppreference 참고
- Lock - mutex가 이미 Lock상태일 때 다시 Lock을 거는 행위는 Undefined 되어있다.
- Unlock - mutex를 Unlock할 때 Lock되어있지 않는 행위는 Undefined 되어있다.
Example Code
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
struct MInt
{
std::mutex mtx;
int num = 0;
};
void plus1(MInt& mi)
{
mi.mtx.lock();
// mi.mtx.lock(); // 1. 락을 2번 거는 행위
mi.num++;
mi.mtx.unlock(); // 2. 락을 걸지 않았는데 unlock을 시도
}
int main()
{
MInt mi;
std::thread t1(plus1, std::ref(mi));
std::thread t2(plus1, std::ref(mi));
t1.join();
t2.join();
std::cout << "num : " << mi.num << std::endl;
return 0;
}
위 코드는 2개의 thread를 생성해서 num을 각각 증가시키는 코드이다. mutex로 보호되고 있기 때문에 데이터레이스가 일어나지 않는다. 하지만 코드 상 1, 2 번 주석과 같은 행위를 하는 경우 undefined 되어있기 때문에 주의해야한다.
try_lock()

또한, mutex에는 try_lock() 함수가 존재한다. Lock을 성공적으로 획득하면 true 그렇지 않으면 false를 리턴한다.
즉, Lock을 획득하지 못했다면 Block 상태로 넘어가는 것이 아니라 바로 false를 리턴하고 제 갈길을 간다.
자세한 내용은 cppreference의 예제 코드를 살펴보도록 한다.
그런데 실제 개발에서는 이러한 lock, unlock 같은 코드를 직접적으로 사용하면 안된다. 일반적인 lock,unlock과 같은 함수를 사용하게 되면 버그가 들어가기 매우 쉽기 때문이다. 예를 들면, 코드가 길어지면서 실수로 lock을 걸어놓고 unlock을 하지 않는경우, lock 이후에 바로 리턴을 하는 경우, 크리티컬 섹션에서 throw를 하는 경우 등등이 있을 수 있다. 이는 mutex를 unlock하기 전에 함수가 해제되면서 해당 mutex가 해제되지 않는 문제가 발생한다.
이를 해결할 수 있는 방법 중 하나는 std::lock_guard를 사용하는 것이다.
Example Code
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
struct MInt
{
std::mutex mtx;
int num = 0;
};
void plus1(MInt& mi)
{
const std::lock_guard<std::mutex> lck(mi.mtx);
mi.num++;
}
int main()
{
MInt mi;
std::thread t1(plus1, std::ref(mi));
std::thread t2(plus1, std::ref(mi));
t1.join();
t2.join();
std::cout << "num : " << mi.num << std::endl;
return 0;
}
std::lock_guard를 생성함과 동시에 mutex를 넘겨주면 된다. 그럼 자동적으로 그 아래 구간이 크리티컬 섹션으로 지정이 되면서 해당 스코프가 끝나면 mutex가 해제된다. 즉, lock_guard를 사용한다면 크리티컬 섹션안에서 리턴을 하든지 throw를 하든지 하는 상황과는 상관없이 스코프가 끝날 때 항상 mutex lock이 끝나는 것을 보장할 수 있다. 이러한 방법을 사용하면 mutex를 훨씬 더 안전하게 사용할 수 있다.
mutex를 사용하고자 할 때는 RAII idom을 따르는 다음과 같은 lock manage를 사용하는 것이 좋다.
- std::lock_guard
- std::scoped_lock
- std::unique_lock
- std::shared_lock
절대 일반적인 mutex에 lock과 unlock을 직접적으로 사용하는 일이 없도록 해야한다.
'C++ > Thread' 카테고리의 다른 글
[Thread] Deadlock, std::scoped_lock (0) | 2022.07.17 |
---|---|
[Thread] std::unique_lock (0) | 2022.07.17 |
[Thread] Mutex(뮤텍스) (0) | 2022.07.15 |
[Thread] std::jthread (조이닝 쓰레드) (0) | 2022.07.13 |
[Thread] thread_local (0) | 2022.06.20 |