수업
-
이미지 1: 식당과 프로그램의 유사성.

- 각 레스토랑은 프로그램.
- 각 직원(로봇)은 쓰레드.
- 영혼은 CPU 코어.
-
이미지 2: 한식, 일식, 패밀리 레스토랑의 비교.

- 쓰레드가 많아질수록 코어와의 적절한 매칭이 중요.
-
이미지 3: 게임 로직, 클라이언트 세션, DB 쓰레드.

-
이미지 4: 그림판, 메모장, MMO 서버.

-
이미지 5: 힙과 스택 메모리 구조.

- 스택은 쓰레드마다 보유.
- 힙은 모든 쓰레드가 공유.
-
이미지 6: "이론 vs 현실"의 차이.

- 이상적으로 쓰레드가 분배되지 않는 현실을 보여줌.
1. 멀티쓰레드란?
멀티쓰레드(Multi-threading)란 하나의 프로그램(프로세스) 내에서 여러 개의 작업(쓰레드)을 동시에 실행하는 기술입니다.
하지만 CPU 코어의 개수보다 많은 쓰레드가 동시에 실행될 수 없으며, 운영체제의 스케줄러(Scheduler) 가 쓰레드 간 실행 시간을 분배하여 빠르게 전환하기 때문에 동시에 실행되는 것처럼 보이는 것입니다.
📌 비유: 멀티쓰레드 = 식당 운영
- 프로그램 → "식당"
- 쓰레드 → "직원"
- CPU 코어 → "직원의 영혼(일할 수 있는 능력)"
즉, CPU 코어가 없으면 직원(쓰레드)은 일을 하지 못하며, 한 명의 직원(쓰레드)은 동시에 여러 테이블을 서빙할 수 없습니다.
2. 단일 쓰레드 vs 멀티쓰레드
💡 "단일 쓰레드"
- 직원이 한 명뿐이라 모든 손님을 차례로 응대해야 함 → 처리가 느림
- 식당(프로그램)이 바빠지면 손님(작업)이 쌓임 → 대기 시간이 길어짐
💡 "멀티쓰레드"
- 여러 직원(쓰레드)이 동시에 일함 → 작업이 빠르게 진행됨
- 손님이 많아도 여러 직원이 나눠서 처리 → 대기 시간이 줄어듦
🚀 게임 서버에서 적용
- 단일 쓰레드 서버 → 하나의 요청이 끝나야 다음 요청을 처리
- 멀티쓰레드 서버 → 여러 요청을 동시에 처리 가능
3. CPU 코어와 쓰레드의 관계
(1) 단일 코어, 다중 쓰레드
- 단일 CPU 코어에서 여러 쓰레드가 빠르게 교체되면서 실행됨.
- 실제로는 한 번에 하나의 쓰레드만 실행 가능하지만, 스케줄링을 통해 마치 동시에 실행되는 것처럼 보임.
(2) 멀티 코어, 멀티 쓰레드
- 여러 코어가 있으면 실제로 여러 개의 쓰레드가 동시에 실행될 수 있음.
- 코어 개수만큼 쓰레드를 병렬 실행할 수 있으며, 나머지 쓰레드는 기존처럼 빠르게 교체됨.
📌 CPU 코어가 많으면?
✅ 더 많은 작업을 동시에 실행 가능!
✅ 하지만 쓰레드가 너무 많으면 자원을 두고 경쟁하여 성능이 저하될 수 있음.
➡ 따라서, 적절한 쓰레드 개수를 유지하는 것이 중요!
4. 게임 서버에서의 쓰레드 관리
게임 서버는 여러 작업을 동시에 처리해야 하므로 멀티쓰레드 환경이 필수입니다.
💡 예제: 게임 서버의 쓰레드 역할 분담
| 역할 | 쓰레드 종류 |
|---|
| 클라이언트 연결 처리 | 네트워크 쓰레드 |
| 게임 로직 처리 | 게임 로직 쓰레드 |
| 데이터베이스 연동 | DB 쓰레드 |
| AI 및 NPC 행동 | AI 쓰레드 |
🎯 목표: 쓰레드를 적절하게 분배하여 서버의 성능을 극대화하는 것!
5. 멀티쓰레드의 메모리 구조
멀티쓰레드를 사용할 때 각 쓰레드는 메모리를 어떻게 공유할까?
(1) 스택(Stack)
- 각 쓰레드마다 독립적인 메모리 공간을 가짐.
- 지역 변수 및 함수 호출 정보를 저장.
- 다른 쓰레드와 공유되지 않으므로 데이터 충돌이 발생하지 않음.
(2) 힙(Heap)
- 모든 쓰레드가 공유하는 메모리 영역.
- 동적으로 할당된 객체들이 저장됨.
- 여러 쓰레드가 동시에 접근하면 Race Condition(경쟁 상태) 발생 가능!
📌 Race Condition (경쟁 상태)
- 여러 쓰레드가 동시에 같은 데이터를 변경하려고 할 때 예상치 못한 결과가 발생하는 현상.
- 예: 두 명의 직원이 동시에 같은 테이블의 계산서를 수정하려 하면 충돌 발생.
➡ 해결 방법
- Mutex (뮤텍스, 상호 배제): 한 번에 하나의 쓰레드만 데이터에 접근하도록 제한.
- Atomic 변수: 특정 연산을 원자적으로 수행하여 중간 상태를 방지.
6. 멀티쓰레드에서 발생하는 문제점과 해결 방법
(1) Deadlock (교착 상태)
- 두 개 이상의 쓰레드가 서로가 필요한 리소스를 점유한 채 기다리면서 멈추는 현상.
- 예: A 직원이 계산서 정리를 기다리고 있고, B 직원은 테이블 정리를 기다리지만 서로 교차된 작업을 점유한 상태라 진행 불가.
📌 해결 방법
✅ Lock 순서 정하기: 항상 같은 순서로 락을 획득하도록 설계.
✅ 타임아웃 설정: 일정 시간 대기 후 락을 해제하도록 구현.
(2) Context Switching (문맥 전환 비용)
- 운영체제가 쓰레드를 교체할 때, 현재 쓰레드의 상태를 저장하고 새로운 쓰레드를 로드하는 과정에서 발생하는 비용.
- 쓰레드가 많을수록 문맥 전환이 잦아지고 성능이 저하될 수 있음.
📌 해결 방법
✅ 쓰레드 개수를 CPU 코어 수에 맞게 적절히 조절.
✅ 필요할 때만 쓰레드를 생성하고, 사용이 끝나면 정리.
7. 멀티쓰레드의 실무 적용
멀티쓰레드를 사용할 때 고려해야 할 몇 가지 중요한 개념들이 있습니다.
(1) 동기화(Synchronization)
여러 쓰레드가 같은 데이터를 수정할 때 데이터 손상을 방지하기 위해 동기화가 필요합니다.
✅ Mutex (뮤텍스): 한 번에 하나의 쓰레드만 리소스 접근 가능
✅ SpinLock: 빠르게 반복해서 락을 확인
✅ Condition Variable: 특정 조건이 만족될 때까지 쓰레드 대기
(2) 스레드 풀(Thread Pool)
- 필요할 때마다 새 쓰레드를 생성하는 것은 비효율적이므로, 미리 생성된 쓰레드 그룹을 사용.
- 요청이 오면 대기 중인 쓰레드가 작업을 수행하고, 완료 후 다시 대기 상태로 돌아감.
➡ 스레드 풀을 활용하면?
✅ 스레드 생성 및 제거 비용 감소
✅ CPU 리소스 활용 최적화