[Server Programming/cpp] Lock 기초
스레드 환경에서 기본 STL자료형을 사용한 경우
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
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;
}
위 코드를 실행해보자. 에러가 뜬다.
기본적으로 지금까지 알던 기본 stl 자료형들은 멀티 스레드 환경에서 제대로 동작하지 않는다고 생각해야한다.
위와 같이 벡터 자료형의 경우에도 push를 사용할때, 단순하게 push하고 끝이 아니다.
이는 여러가지의 과정을 거치게 되는데 이 과정 진행 중에 다른 스레드에서 push를 진행하게 되면 중간 과정 안에서 꼬이게 될 수 밖에 없다..
실패한 해결법 예제
atomic<vector<int32>> v;
...
v.reserve(20000);
올바른 해결 방법이 아니다.
lock을 사용한 해결
mutex lock의 문제점
mutex m;
void Push() {
for (int32 i = 0; i < 10000; i++)
{
m.lock(); // 자물쇠 잠그기
v.push_back(i);
m.unlock(); // 자물쇠 풀기
}
}
이제 메인 코드가 잘 작동하지만 lock개념에 대해 몇가지 짚고 가야할 문제들이 있다.
첫번째, 추가적인 재귀적인 lock이 필요할 경우 불가능하다. -> recursive lock이 필요
두번째, lock이 되었으나 unlock을 하지 않는 경우 -> 무한 대기에 걸려 크리티컬 에러 발생
lock guard를 이용한 lock
//RAII패턴
//mutex를 자동으로 lock해주고 unlock해주는 wrapper 클래스
template<typename T>
class LockGuard {
public:
LockGuard(T& m) {
_mutex = &m;
_mutex->lock();
}
~LockGuard() {
_mutex->unlock();
}
private:
T* _mutex;
};
...
LockGuard<std::mutex> lockGuard(m);
주석 설명대로 클래스의 객체가 생성되면 뮤텍스를 lock해줄 것이고 스코프를 벗어나 소멸될때 알아서 소멸자가 실행되며 mutex를 언락해주며 함께 폭파할것이다!
실제로는 std::lock_guard를 사용하면 되겠다.
unique lock
...
unique_lock<std::mutex> lockGuard(m,std::defer_lock);
//m.lock(); // 자물 잠그기
lockGuard.lock();
unique lock이란 것도 있는데, 조건이 하나 추가된 형태이다. 락 가드 처럼 객체 생성시 바로 뮤텍스가 설정되는것이 아닌 이후에 lock을 직접걸때부터 동작이 된다.
이후 소멸할때는 기존의 락가드와 같이 동작한다.
출처
인프런 Rookiss, "[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버"