컨텍스트 스위칭은 운영체제가 CPU를 현재 프로세스에서 다른 프로세스로 전환(switch) 할 때, 이전 상태를 저장하고 다음 상태를 복원하는 작업이야.
스레드는 같은 프로세스 내에서 자원을 공유하기 때문에 스위칭 시 저장하고 복원해야 할 정보가 적어져서 비용이 훨씬 낮아.
| 자원 종류 | 프로세스끼리 공유? | 스레드끼리 공유? |
|---|---|---|
| 코드/데이터/힙 | ❌ X | ✅ O |
| 파일 디스크립터 | ❌ X | ✅ O |
| 레지스터/PC/스택 | ❌ X (각자 가짐) | ❌ X (각자 가짐) |
→ 즉, 스레드는 "코드/데이터/파일"은 공유하지만 스택과 레지스터만 따로 가지고 있어.
| 항목 | 프로세스 간 스위칭 | 스레드 간 스위칭 |
|---|---|---|
| CPU 레지스터 저장/복원 | ✅ | ✅ |
| 가상 메모리 맵 교체 | ✅ (필수) | ❌ (같은 주소 공간) |
| TLB (주소 변환 캐시) flush | ✅ | ❌ |
| 파일 디스크립터 교체 | ✅ | ❌ |
| 캐시 무효화 가능성 | 높음 | 낮음 |
→ 그래서 스레드 간 스위칭은 더 빠르고, 부하가 적어!
스레드는 하나의 프로세스 안에서 실행되기 때문에, 메모리 공간은 공유해.
| 구분 | 공유 | 독립 |
|---|---|---|
| Code | ✅ | ❌ |
| Data (Heap, 전역 변수) | ✅ | ❌ |
| Stack | ❌ (스레드마다 개별 스택 가짐) | ✅ |
스레드는 같은 프로세스 안의 데이터, 힙 메모리, 파일을 공유해.
즉, 이런 상황이 가능한 거야:
Thread A: 변수 x = 3으로 바꿈
Thread B: 변수 x를 읽음 → (3으로 바뀐 걸 봐버림)
→ 동시에 접근하면 어떤 값이 저장될지 예측 불가!
두 개 이상의 스레드(또는 프로세스)가 동시에 같은 자원에 접근하고, 그 중 적어도 하나가 값을 변경할 때, 실행 순서에 따라 결과가 달라지는 현상.
예시:
// 공유 변수
int count = 0;
// 두 스레드가 동시에 이 함수 호출
void increment() {
count = count + 1;
}
count == 2count == 1 ← 중간에 덮어쓰기 생김| 스레드 A | 스레드 B | counter |
|---|---|---|
| 읽기: 0 | 0 | |
| 읽기: 0 | 0 | |
| 더함: 1 | ||
| 더함: 1 | ||
| 쓰기: 1 | 1 | |
| 쓰기: 1 | 1 ❌ (실제로는 2여야 함!) |
이런 "엉켜서 실행되는" 상태가 바로 경쟁 조건이야.
| 항목 | 설명 |
|---|---|
| 재현 어렵다 | 실행 타이밍에 따라 간헐적으로 발생 |
| 디버깅 어렵다 | 단일 스레드로 돌리면 문제 안 생김 |
| 해결책 필요 | 동기화 기법 (뮤텍스, 세마포어, 락 등)이 필요 |
| 도구 | 역할 |
|---|---|
| Mutex (뮤텍스) | 한 번에 하나의 스레드만 자원 접근 허용 |
| Semaphore | 여러 개 중 일부만 접근 허용 |
| Atomic 연산 | 경쟁 없이 값을 읽고 쓰는 특별한 연산 |
pthread_mutex_t lock;
void* worker(void* arg) {
for (int i = 0; i < 1000; ++i) {
pthread_mutex_lock(&lock);
counter = counter + 1;
pthread_mutex_unlock(&lock);
}
}
이렇게 하면 한 번에 하나의 스레드만 counter를 바꿀 수 있게 돼 → 경쟁 조건 해결!