게임 서버 프로그래밍 교과서를 읽으면서 정리한 내용입니다.
-
program (code, data) on disk => 실행 => process (code + data + heap + stack) on RAM
-
multithreading : 다수의 thread가 하나의 process에서 실행되는 것
-
thread : process처럼 command를 한 줄씩 실행하는 기본 단위
- thread는 하나의 process 안에 하나 이상 존재
- thread는 pocess 안에 있는 memory을 공유
- 각 thread마다 stack을 가짐 (각 thread에서 실행되는 function의 local variable들이 thread마다 존재)
-
C/C++로 작성된 program은 void main()를 시작점으로 많은 function 혹은 subroutine 실행
- function은 실행이 끝나면 자기 자신을 호출한 caller로 되돌아가고, 이 정보는 call stack에 저장
- 각 thread는 각자의 call stack을 가짐
- thread를 생성하려면 OS나 runtime에서 제공하는 생성용 함수를 호출 (이때 argument로 thread가 최초로 실행할 function 및 parameters를 넣어줌)
std::thread t1(ThreadProc, 123);
- thread 생성 함수를 실행하면 시작 -> command를 실행하는 Runnable 상태로 바뀌었다가, 다른 무언가를 기다릴 때에는 Blocked 상태가 됨 -> 모든 일을 마치면 Dead
-
multithreading이 유용한 경우?
- 하나의 오래 걸리는 일과 여러 빨리 끝나는 일을 같이 해야 할 때 e.g) 렌더링과 데이터 로딩을 동시에 하는 경우
- 어떤 긴 처리를 진행하는 동안 다른 짧은 일을 처리해야 할 때 e.g) 사용자 정보를 I/O하거나 disk에 접근할 때 생기는 interrupt를 효율적으로 사용해야 하는 경우
- 기기에 있는 CPU를 모두 활용해야 할 때 e.g) core를 하나만 사용하는 thread 특성을 생각하면 single thread는 비효율적
-
context switching: OS는 여러 process와 그 안의 thread들을 일정 시간마다 번갈아 실행
- 실행 중인 thread의 call stack을 저장하고, 다른 thread 중에 하나를 골라 call stack을 복원하고 실행 지점으로 이동하는 연산의 양이 많음 -> 스위치 구간이 잦으면 실제 실행에 할애되는 시간이 그만큼 적어짐
- context switching의 실행은 사용자 입장에서 쾌적할 수 있는 가급적 긴 시간 단위로 이루어짐 -> time slace
- context switching의 발생은 CPU 개수 < thread 개수일 때 발생, 그렇지만 OS 대부분은 CPU 개수 < thread 개수인데 문제가 없는 이유는 Runnable 상태의 thread가 CPU보다 많을 때에만 문제임
- program이 어디까지 실행된 이후에 context switching이 발생하는지?: context switch는 기계어 단위로 발생하는데, 하나의 구문은 여러 기계어로 컴파일되므로 context switch가 발생
-
data race: 여러 thread가 data에 접근해서 그 상태를 예측할 수 없게 되는 것
- 여러 thread가 접근하는 member variable는 하나의 thread가 접근할 때에는 다른 thread가 접근하지 못하게 하는 원자성이 보장하여, 일관성 있는 상태를 유지해야 함
-
data race를 방지하기 위해 member variable의 원자성과 일관성을 유지하는 조치를 synchronize라고 함
- mutex : 상호 배제를 통해 data race 해결
std::mutex mx;
mx.lock();
read(x);
write(x);
sum(x);
mx.unlock();
- 매번 lock()과 unlock()을 호출하는 것은 번거롭기도 하고, 예외 처리 코드 또한 작성하기 어렵기에 뮤텍스 잠금 상태를 local variable로 저장하고, 그 변수가 사라질 때 잠금 해제를 자동으로 되게 하는 클래스 lock_guard 제공
std::recursive_mutex mx;
lock_guard<recursive_mutex> lock(mx);
read(x);
write(x);
sum(x);