[Networking] 03 멀티 스레딩
Written on September 7th, 2023 by Imeamangryang
해당 포스팅은 배현직님의 [게임 서버 프로그래밍 교과서] 책의 내용을 바탕으로 작성되었습니다.
1.6 Mutex & Lock
“스레드에서 어떤 자원을 사용하는 동안 다른 스레드가 해당 자원을 건드리지 못하게 하려면 어떻게 해야 할까?”
운영체제에서는 이러한 문제들을 위해서 Mutex(Mutual Exclusion), Lock, Semaphore와 같은 기능들을 지원한다.
// C++에서의 간단한 Mutex 사용 예시
std::mutex mx;
mx.lock();
read(x);
write(y);
mx.unlock();
// 또는
std::mutex mx;
{
// lock_guard를 이용하면 변수가 사라질 때 자동으로 잠금 해제가 된다.
std::lock_guard<std::mutex> lock(mx);
read(x);
write(y);
}

그렇다면 Mutex와 같은 잠금은 어떻게 활용해야 할까? 잠금을 적절하게 사용하는 것도 매우 중요하다.
만약 프로그램 내부 전체 데이터를 한꺼번에 보호하는 잠금을 만든다면 어떻게 될까. 그 프로그램은 싱글스레드로 작동하는 프로그램과 다를 바가 없을 것이다.
따라서 잠금 범위도 코드에 따라 적절하게 나누는 것이 바람직하다. 하지만 만약 잠금을 잘못 걸어버린다면 DeadLock(교착)상태에 빠질 수도 있다.
1.7 DeadLock
다음 코드를 보자.
int a, b;
Thread1()
{
lock(a);
{
a++;
lock(b);
{
b++;
}
}
}
Thread2()
{
lock(b);
{
b++;
lock(a);
{
a++;
}
}
}
컨텍스트 스위치로 두 스레드를 번갈아 실행 중이라고 했을 때, 만약 Thread1에서 a를 잠근 뒤 Thread2로 스위칭이 일어나 b를 잠가버린다면 어떤 일이 벌어질까.
결론은 Thread1에서는 Thread2에서 b를 풀어줄 때까지 기다려야 하는데 Thread2에서는 Thread1에서 a를 풀어줄 때까지 기다려야 한다.
두 스레드 모두 상대방의 작업이 끝나기를 기다리지만 끝이 날 방법이 없다. 이것을 DeadLock(교착 상태)이라고 한다.

DeadLock이 발생하면 프로그램에 심각한 피해가 생기므로 이러한 문제를 예방하기 위해서라도 잠금을 어떻게 활용할 지 고민하는 것은 중요한 문제이다.
뮤텍스가 여럿일 때 교착 상태를 일으키지 않게 하기 위해서는 Critical Section(임계 영역)에 따라 적절히 잠금을 걸고 잠금 순서의 규칙에 따라 잠금하고 해제하는 것이 중요하다.