에듀테크 프로젝트 대시보드
경쟁 상태와 동기화
두 스레드가 같은 변수를 동시에 고치면, 코드는 멀쩡해 보여도 결과가 매번 달라진다. 왜 그런지, 그리고 임계 구역과 락(뮤텍스)이 어떻게 막는지 눈으로 보자.
count++ 는 한 번에 끝나지 않는다고급 언어의 count = count + 1 한 줄은 CPU 입장에서는 최소 세 단계다.
// count++ 가 실제로 하는 일 LOAD reg ← 메모리에서 count 값을 레지스터로 읽기 INC reg ← 레지스터 값에 1 더하기 STORE reg → 레지스터 값을 메모리의 count 에 쓰기
운영체제는 아무 단계 사이에서나 스레드를 멈추고 다른 스레드로 전환(context switch)할 수 있다. 두 스레드가 거의 동시에 LOAD 하면, 둘 다 같은 옛날 값을 읽고, 각자 1을 더해, 각자 써넣는다 — 두 번 더했는데 1만 증가한다. 이렇게 사라진 갱신을 잃어버린 갱신(lost update)이라 한다.
스레드 A와 B가 각각 공유 변수 count 를 5번씩 증가시킨다. 기대값은 10. 한 단계씩 실행하며 어느 스레드가 어느 명령을 수행하는지 골라 보고, 자동 실행으로 무작위 스케줄을 반복 관찰해 보자.
문제가 생기는 LOAD → INC → STORE 구간을 임계 구역(critical section)이라 부른다. 규칙은 하나 — 임계 구역 안에는 한 번에 스레드 하나만. 이걸 상호 배제(mutual exclusion)라 하고, 이를 강제하는 도구가 락이다.
lock.acquire() # 이미 잡혀 있으면 여기서 대기(block) count = count + 1 # ← 임계 구역: 나 혼자만 있음이 보장됨 lock.release() # 대기하던 다른 스레드를 깨움
위 데모에서 "락으로 보호"를 켜면, 한 스레드가 LOAD 하는 순간 락을 쥐고, STORE 를 마칠 때까지 다른 스레드는 blocked 상태로 얼어붙는다. 어떤 순서로 실행하든 결과는 항상 10.
| 도구 | 핵심 |
|---|---|
| Mutex (뮤텍스) | 0/1 상태의 락. "잠금을 건 스레드"만 풀 수 있음. 임계 구역 보호에 사용. |
| Semaphore (세마포어) | 정수 카운터. 자원 N개를 N개 스레드까지 동시 허용. P(감소·대기)/V(증가·신호). |
| Atomic 연산 | CPU가 LOAD+INC+STORE를 쪼개지지 않는 한 명령으로 실행 (CAS 등). 락 없이도 안전. |
+1 해서 숫자가 조금씩 샌다.해결의 위치는 다양하다 — 코드에서는 뮤텍스/원자적 연산, 데이터베이스에서는 트랜잭션과 행 잠금, 분산 환경에서는 분산 락이나 단일 소유자로 직렬화한다.