[게임 서버] 1.7 교착 상태
카테고리: GameServer
태그: GameServer
이 글은 아래의 책을 자세히 정리한 후, 정리한 글을 GPT에게 요약을 요청하여 작성되었습니다.
게임 서버 프로그래밍 교과서, 배현직 저자
📦 1. 멀티스레딩
👉🏻 교착 상태
int a;
int b;
// 스레드 1
Thread() {
lock(a) { // 1
a++;
lock(b) { // 3
b++;
}
}
}
// 스레드 2
Thread() {
lock(b) { // 2
b++;
lock(a) { // 4
a++;
}
}
}
두 스레드를 번갈아 실행하다가 1 → 2 → 3 → 4 순으로 실행되면,
스레드 1은 b의 잠금을 기다리고, 스레드 2는 a의 잠금을 기다리는 상태가 된다.
→ 서로 상대가 락을 풀기만을 기다리는 상태, 즉 교착 상태(deadlock)
1️⃣ 교착 상태가 되면?
- CPU 사용량은 0%에 가까움
- 클라이언트 요청에 응답 없음
- 동시에 접속한 사용자가 많든 적든 무조건 멈춘다
- 서버는 켜져있지만 동작은 안 한다
2️⃣ 원인 찾기
- Windows에서 제공하는
CRITICAL_SECTION
은 내부 상태를 확인할 수 있다 - 디버거에서 해당 객체 정보를 보면,
→ 어떤 스레드가 어디서 멈췄는지 추적 가능
3️⃣ CRITICAL_SECTION 클래스 구현
class CriticalSection {
CRITICAL_SECTION m_critSec;
public:
CriticalSection();
~CriticalSection();
void Lock();
void Unlock();
};
class CriticalSectionLock {
CriticalSection* m_pCritSec;
public:
CriticalSectionLock(CriticalSection& critSec);
~CriticalSectionLock();
};
CriticalSection::CriticalSection() {
InitializeCriticalSectionEx(&m_critSec, 0, 0);
}
CriticalSection::~CriticalSection() {
DeleteCriticalSection(&m_critSec);
}
void CriticalSection::Lock() {
EnterCriticalSection(&m_critSec);
}
void CriticalSection::Unlock() {
LeaveCriticalSection(&m_critSec);
}
CriticalSectionLock::CriticalSectionLock(CriticalSection& critSec) {
m_pCritSec = &critSec;
m_pCritSec->Lock();
}
CriticalSectionLock::~CriticalSectionLock() {
m_pCritSec->Unlock();
}
🔹 핵심 요약
- 생성:
InitializeCriticalSectionEx
- 삭제:
DeleteCriticalSection
- 잠금:
EnterCriticalSection
- 해제:
LeaveCriticalSection
CriticalSectionLock
클래스는 RAII 방식으로 자동 잠금/해제를 보장
RAII 방식에 대해선 [C++] 항목 13: 자원 관리에는 객체가 그만! 에서 알아볼 수 있다.
💡 교착 상태를 재현하는 코드 예제
int a;
CriticalSection a_mutex;
int b;
CriticalSection b_mutex;
int main() {
thread t1([]() {
while (1) {
CriticalSectionLock lock(a_mutex);
a++;
CriticalSectionLock lock2(b_mutex);
b++;
cout << "t1 done.\n";
}
});
thread t2([]() {
while (1) {
CriticalSectionLock lock(b_mutex);
b++;
CriticalSectionLock lock2(a_mutex);
a++;
cout << "t2 done.\n";
}
});
t1.join();
t2.join();
return 0;
}
🔹 실행 흐름:
- t1은 a → b 순으로 락
- t2는 b → a 순으로 락
- 둘이 동시에 락을 걸고 있다가 서로가 가진 락을 기다리는 상황이 오면 교착 상태
출력 예시:
t1 done.
t1 done.
t1 done.
...
(이후 멈춤)
→ 프로그램은 죽지 않지만, 진행이 멈춘다
⚠️ 이번 정리에선 디버거 실습은 생략 대신 교착 상태가 어떻게 생기는지, 코드와 함께 원리 위주로 설명
🧐 정리
- 교착 상태는 서로가 필요한 락을 이미 상대가 가지고 있을 때 발생
- 이 상태에선 프로그램이 응답을 멈추고, CPU 사용량도 거의 없다
- RAII 패턴을 이용해 락 해제를 자동화하면 실수 줄일 수 있다
댓글남기기