1. 프로그램과 프로세스

프로그램
코드와 데이터로 구성된 명령어 모음이 들어 있는 데이터 덩어리
프로세스
프로그램을 실행하여 명령어가 한 줄씩 실행되며 활동하는 상태
프로그램의 코드와 데이터는 로딩되어 프로세스 메모리로 불러들이게 된다. 프로세스 메모리에는 힙과 스택도 공존한다. 스택에는 현재 실행 중인 함수들의 호출 기록과 사용중인 로컬 변수들이 들어 있다.
프로그램은 저장 저장소에 존재하며, 프로세스는 RAM에 존재한다.

같은 프로그램을 여러 개 동시에 실행할 수 있는데, 이와 같이 프로세스가 여러 개 실행되고 있는 것을 멀티 프로세싱이라 한다.
2. 스레드
스레드
명령어를 한 줄씩 실행하는 기본 단위

- 스레드는 한 프로세스 한에 여러 개가 있다
- 한 프로세스 안에 있는 스레드는 프로세스 안에 있는 메모리 공간을 공유한다
- 스레드마다 스택을 가진다. 각 스레드에서 실행되는 함수의 로컬 변수가 스레드 마다 있다는 것
싱글 스레드 프로그램
하나의 스레드만 실행되는 프로그램, 싱글스레드로만 작동하도록 프로그램을 설계하고 구현하는 것을 싱글스레드 모델이라 한다.

멀티 스레드 모델 / 멀티스레딩
프로세스 안에서 여러 스레드를 생성하여 동시에 진행시키는 것

각 스레드는 실행 지점이 서로 다르며, 스레드를 실행할 때는 호출되는 함수가 다르기에 해당 스레드가 최초로 실행할 함수를 지정해주어야 한다. 그렇기에 각 스레드는 각자 호출 스택을 가지고 있다.
같은 함수를 실행하더라도 함수에 넘긴 인자, 메모리 상태가 다를 수 있고, 같은 함수, 인자, 메모리라도 실행 지점이 달라질 수 있다.
스레드를 생성하려면 해당 환경에서 제공하는 스레드 생성 함수를 호출한다. 이때 함수 인자로 스레드가 최초 실행할 함수, 해당 함수가 받아들일 매개변수를 넣어야 한다.
int main()
{
t1 = CreateThread(ThreadFunc, 1);
// main thread func
FUNC A
t1.Join(); // wait t1 thread
}
ThreadFunc(int a)
{
// something...
FUNC B
}
위와 같이 스레드를 실행해볼 수 있다.
이때 핵심은 FUNC A, B중 누가 먼저 실행될지는 알 수 없다는 것이다.
메인 스레드에서는 FUNC A가 작동중이며, t1 스레드에서는 FUNC B가 작동중인 상태이며, 서로 먼저 작업을 끝낼시 상대를 기다리게 된다(Join()).
t1 스레드의 경우 함수 내에서 작업이 끝나고 return 될시 자연스럽게 소멸하게 된다.
만약 Join없이 = 스레드 작업 완료를 대기하지 않고 메인 스레드가 종료될시 좀비 프로세스가 된다.
좀비 프로세스
메인 스레드가 종료되었지만 다른 스레드가 남아있는 현상
3. 멀티스레드 프로그래밍은 언제 해야 하는가?
멀티스레드 프로그래밍은 왜 사용해야 하는가에 대한 대답이 분명할때 사용하여야 한다.
- 오래 걸리는 일 하나와 빨리 끝나는 일 여러개를 처리할때
- 기기에 있는 CPU를 모두 활용해야 할 때
3.1 오래 걸리는 일 하나와 빨리 끝나는 일 여럿을 같이 할때
대표적으로 게임의 로딩이 있다.
게임을 시작할때 캐릭터, 배경 등 리소스를 로딩하기 위해서 많은 양의 데이터를 디스크에서 가져와야 한다. 이때 얼마나 가져왔는지를 GUI로 표시해준다.
이때 만약 멀티 스레딩이 없다면 이하와 같이 구현되어야 한다.
LoadScene()
{
RenderGUI();
LoadData(); // 그래픽
RenderGUI();
LoadData(); // 모델
RenderGUI();
LoadData(); // 텍스쳐
RenderGUI();
LoadData(); // ETC...
RenderGUI();
LoadData();
}
이렇게 구현된다면 큰 파일을 가져올때마다 일시적으로 GUI가 멈추게 된다.
이때에 멀티 스레드를 활용하면 좀 더 효율적으로 구현할 수 있다.
bool isLoad = true;
ThreadA()
{
while(isLoad)
{
FrameMove();
Render();
}
}
ThreadB()
{
LoadData();
LoadData();
LoadData();
LoadData();
LoadData();
isLoad = false;
}
A 스레드에서는 렌더링을 계속 처리하게 한다.
B 스레드에서는 게임에 필요한 데이터를 디스크에서 가져온다.
두 가지 일을 동시에 처리하기 때문에 로딩이 진행되는 동안 부드러운 화면 진행을 구현할 수 있다.
3.2 기기에 있는 CPU를 모두 활용해야 할 때
200년 중반까지는 CPU의 실행 속도, 클록 수가 증가 했지만, 이후부터는 CPU 클록 대신 코어를 늘리는 방향으로 성능을 증가시키고 있다. 즉, CPU의 자체 속도는 증가하지 못하고 현대에 4GHz 속도에 머물고 있다.
스레드는 코어를 하나만 사용하고, 만약 싱글 스레드로 프로그램을 구현했다면 현대 기준 CPU의 성능을 온전히 끌어 사용하지 못하는 상황이 된다.
만약 A라는 작업을 4코어 CPU로 처리한다고 가정할때에, 싱글 스레드 프로그램으로 구현하였다면 A 작업에 1개의 코어만 사용되고 나머지 3개의 코어 성능은 버려졌다고 표현할 수 있다.
다만 이하와 같이 코드를 짜게 된다면 문제가 발생할 수 있다.
void ThreadFunc(int& num)
{
for (int i = 0; i < 1000000; i++)
{
num++; // 여러 스레드가 동시에 접근 → 데이터 경쟁
}
}
int main()
{
int num = 0;
std::thread threads[4];
for (int i = 0; i < 4; i++)
{
threads[i] = std::thread(ThreadFunc, std::ref(num));
}
for (auto& t : threads)
{
t.join(); // 각 스레드가 끝날 때까지 기다림
}
}
여러 스레드가 동시에 num에 접근하려 하고 이때문에 스레드 정체가 일어나게 된다.
4. 스레드 정체
프로세스와 스레드를 관리하는 운영체제는 여러 프로세스와 각 프로세스 안의 스레드를 일정 시간마다 번갈아 가면서 실행한다. 이때 컨텍스트 스위치가 일어난다.
컨텍스트 스위치 Context Switch
스레드를 실행하다 다른 스레드를 마저 실행하는 과정
실행준인 스레드의 상태를 저장하고 이전에 실행하다 만 다른 스레드중 하나를 골라 해당 상태를 복원하고 실행지점으로 이동한다.
컨텍스트 스위치를 하는 데 연산량이 무시 할 수 없는 수준이고, 만약 자주 일어난다면 해당 연산이 오히려 본 프로그램 연산보다 부하가 걸릴 수 있다.
만약 컨텍스트 스위치를 최소화 한다면 작업이 과하게 블록화 되기 때문에 문제가 된다. 그렇기에 컨텍스트 스위치는 타임 슬라이스 단위로 이루어진다.
타임 슬라이스
사람 입장에서 쾌적할 수 있는 시간 단위, 보통 5ms 정도를 의미한다.
CPU 개수와 스레드 개수의 관계
CPU 개수와 스레드 개수가 같거나 스레드 개수가 더 적으면 컨텍스트 스위치가 발생할 이유가 없다. 하지만 스레드 개수가 더 많으면 컨텍스트 스위치가 반드시 어느 CPU 안에서 발생한다.
대부분의 운영체제는 CPU 개수보다 많은 수의 프로세스가 있고, 각 프로세스는 최소 스레드를 최소 1개는 가지고 있다. 이는 성능에 큰 문제가 되어야 하지만, 큰 문제가 되지 않는 이유는 Runnable 상태인 스레드와 Waitable 상태의 스레드에 있다.
Waitable = 대기 상태의 스레드는 연산 성능에 지장을 주지 않는다. 즉, Runnable인 스레드 개수가 CPU보다 많을때 문제가 발생할 수 있다.
컴퓨터의 명령어는 기본적으로 기계어 단위이며 컨텍스트 스위치도 기계어 명령어 단위로 일어난다. 이 때문에 기계어 명령어 중간에 컨텍스트 스위치가 일어나지는 않는다.
다만 언어를 사용하여 프로그래밍을 할 경우 한 구문이 실제 기계어 명령어 여러 개로 컴파일 되기에 구문 실행 중에 컨텍스트 스위치가 일어날 수 있다.
'공부 > 네트워크' 카테고리의 다른 글
| 게임 서버 프로그래밍 - 교착 상태, 잠금 순서의 규칙 (0) | 2026.09.17 |
|---|---|
| 게임 서버 프로그래밍 - 멀티 스레딩 2 / 스레드 주의사항, 뮤텍스 (0) | 2026.09.14 |
| 서버 개발시 사용되는 HTTP Status Code (0) | 2026.09.12 |
| OAuth란 무엇인가? (0) | 2026.05.25 |
| 네트워크 (2025 11 06) (0) | 2025.11.06 |