[게임 서버] 1.5 스레드를 다룰 때 주의 사항

업데이트:     Updated:

카테고리:

태그:

이 글은 아래의 책을 자세히 정리한 후, 정리한 글을 GPT에게 요약을 요청하여 작성되었습니다.
게임 서버 프로그래밍 교과서, 배현직 저자

📦 1. 멀티스레딩

👉🏻 5. 스레드를 다룰 때 주의 사항

⚠️ 경쟁 상태 (데이터 레이스)

두 스레드가 데이터에 접근해서 그 데이터 상태를 예측할 수 없게 되는 상황

예를 들어, 이런 코드가 있다고 해보자.

x += y;

이걸 기계어 수준으로 바꾸면 다음과 같이 된다.

t = x
t = t + y
x = t

자, 이걸 두 개의 스레드가 동시에 실행한다면?


✅ 이상적으로 돌아가는 상황

x = 2
# 스레드 1
t1 = x        // t1 = 2
t1 = t1 + 3    // t1 = 5
x = t1        // x = 5

# 스레드 2
t2 = x        // t2 = 5
t2 = t2 + 4    // t2 = 9
x = t2        // x = 9

이런 식으로 순차적으로 잘 돌아가면 괜찮다. 하지만 현실은 그렇지 않다.


❌ 실제로는 이렇게 될 수도 있다

x = 2

# 스레드 1
t1 = x        // t1 = 2
t1 = t1 + 3    // t1 = 5

# 스레드 2 (도중에 컨텍스트 스위치 발생)
t2 = x        // t2 = 2
t2 = t2 + 4    // t2 = 6
x = t2        // x = 6

# 다시 스레드 1
x = t1        // x = 5 ← ❗ 예상치 못한 값

결과적으로 x = 9가 돼야 할 것 같았는데, x = 5가 되어버렸다. → 바로 이게 데이터 레이스다.


🔍 이전의 소수 구하기 프로그램에서 무슨 문제가 있었을까?

int num = 1;
Array<int> primes;

ThreadProc() {
	while(num <= 1000000) {
		if(IsPrime(num))
			primes.Add(num);
		num++;
	}
}

main() {
	Array<Thread> threads;
	
	for(int i=0; i<4; i++)
		threads.Add(BeginThread(ThreadProc));

	for(int i=0; i<4; i++) {
		threads.WaifForExit(); // thread->join() 같은 역할
	}
	PrintNumber(primes);
}

이 코드는 겉보기엔 문제 없어보이지만, 치명적인 문제 두 가지가 숨어있다.


🧨 문제 1. 여러 스레드가 num에 동시에 접근

  • 여러 스레드가 동시에 num을 읽는다.
  • num을 동시에 증가시킨다.

이건 기계어 수준으로 보면 이렇게 된다.

r1 = num
r1 = r1 + 1
num = r1

즉, 같은 값을 여러 번 처리하거나, 값을 건너뛰는 일이 생길 수 있다.


🧨 문제 2. 여러 스레드가 primes에 동시에 접근

Array<int> primes에는 내부적으로

  • 배열을 가리키는 포인터
  • 배열의 크기 정보

이 두 가지 중요한 멤버가 있다.
프라임 숫자를 추가할 때 공간이 부족하면
→ 메모리를 재할당하게 된다.

그럼 포인터가 가리키는 위치가 바뀌게 되는데, 다른 스레드가 그걸 모른 채 접근하면?
이미 해제된 메모리에 접근하게 되고 → 충돌이 발생한다.


🧷 해결 방법: 동기화

이런 문제를 막기 위해 공유 데이터에 접근하는 순간만큼은 다른 스레드가 접근하지 못하도록 막아야 한다.


🧱 원자성 (Atomicity)

  • 하나의 작업 단위는 절대 쪼개지지 않게 만든다.
  • num++, primes.Add()와 같은 연산은 → 반드시 하나의 원자적인 동작으로 보장해야 한다.

🔗 일관성 (Consistency)

  • 여러 멤버 변수(배열 포인터, 크기)가 항상 서로 어울리는 상태여야 한다.
  • 재할당 중간에 끼어들지 않도록 보호해야 한다.

🔐 이걸 가능하게 하는 기술: 동기화(Synchronization)

  • 임계 영역 (Critical Section)
  • 뮤텍스 (Mutex)
  • 잠금 (Lock)

이런 기법들을 사용하면 멀티스레드 환경에서도 안전하게 데이터를 다룰 수 있다.


즉, 멀티스레딩을 하다 보면 원자성과 일관성을 반드시 고려해야 하고, 이를 위해선 적절한 동기화 기법을 써야 한다.

GameServer 카테고리 내 다른 글 보러가기

댓글남기기