동기화
Mutex & Semaphore
여러 스레드가 동시에 같은 변수를 수정하면 어떻게 될까? 예상치 못한 결과가 나타나는 이 현상을 경쟁 조건(Race Condition)이라 한다. 동기화(Synchronization)는 이 문제를 해결하기 위해 공유 자원에 대한 접근 순서를 제어하는 메커니즘이다.
임계 구역 문제
임계 구역(Critical Section)은 공유 자원에 접근하는 코드 영역이다. 여러 스레드가 동시에 임계 구역에 진입하면 데이터 일관성이 깨진다. 예를 들어 두 스레드가 동시에 counter++를 실행하면 하나의 증가가 무시될 수 있다.
임계 구역 문제를 해결하기 위한 세 가지 조건이 있다.
뮤텍스 (Mutex)
뮤텍스(Mutex, Mutual Exclusion)는 잠금(Lock)/해제(Unlock) 두 가지 상태만을 가진 동기화 객체다. 잠금을 획득한 스레드만이 임계 구역에 진입할 수 있으며, 다른 스레드는 잠금이 해제될 때까지 대기한다.
import threading mutex = threading.Lock() counter = 0 def increment(): global counter mutex.acquire() # lock() try: counter += 1 # 임계 구역 finally: mutex.release() # unlock()
세마포어 (Semaphore)
세마포어는 정수 카운터를 사용하여 동시에 접근 가능한 자원의 수를 제어한다. P 연산(wait)은 카운터를 감소시키고, V 연산(signal)은 증가시킨다. 카운터가 0이 되면 대기 상태에 진입한다.
모든 자원 사용 가능
1개 남음
다음 스레드 대기
이진 세마포어 vs 카운팅 세마포어
이진 세마포어 (0 or 1)
- 뮤텍스와 유사하게 동작
- 단, 소유권 개념 없음
- 다른 스레드가 해제 가능
- 신호 전달(Signaling) 용도에 적합
카운팅 세마포어 (N 이상)
- N개까지 동시 접근 허용
- DB 커넥션 풀 관리에 적합
- 생산자-소비자 문제 해결
- 자원 수를 정확히 관리 가능
뮤텍스 vs 세마포어 비교
| 구분 | 뮤텍스 | 세마포어 |
|---|---|---|
| 상태 | 잠금 / 해제 (2가지) | 정수 카운터 (0 ~ N) |
| 소유권 | 있음 (획득한 스레드만 해제) | 없음 (어느 스레드도 해제 가능) |
| 주 용도 | 단일 자원 상호 배제 | 자원 수 제한, 신호 전달 |
| 재귀 잠금 | 구현에 따라 가능 (Recursive Mutex) | 불가 |
| 성능 | 비교적 빠름 | 상대적으로 느림 |
스핀락 (Spinlock)
스핀락은 잠금이 해제될 때까지 CPU를 점유하면서 반복적으로 확인(Busy Waiting)하는 방식이다. 컨텍스트 스위칭 비용이 없어 잠금 대기 시간이 매우 짧을 때 뮤텍스보다 효율적이다. 그러나 대기 시간이 길면 CPU 자원을 낭비한다.
모니터 (Monitor)
모니터는 뮤텍스와 조건 변수(Condition Variable)를 결합한 고수준 동기화 추상화다. Java의 synchronized, Python의 with lock:이 모니터 패턴을 구현한다. 프로그래머가 직접 잠금/해제를 관리할 필요 없이 언어 수준에서 동기화를 보장한다.
댓글