핵심 요약
여러 스레드가 공유 자원을 동시에 건드리면 데이터가 깨진다. 이를 막는 두 도구가 뮤텍스와 세마포어다. 뮤텍스는 "한 번에 하나만"(열쇠 1개), 세마포어는 "한 번에 N개까지"(자리 N개). 뮤텍스는 잠근 스레드만 풀 수 있는 소유권이 있고, 세마포어는 없다.
1. 비유로 이해
| 뮤텍스 | 세마포어 | |
|---|---|---|
| 비유 | 화장실 열쇠 1개 | 주차장 N자리 |
| 동시 접근 | 1개만 | N개까지 |
| 소유권 | 잠근 스레드만 해제 | 누구나 signal 가능 |
| 주용도 | 임계구역 상호배제 | 자원 개수 제한·신호 |
2. 뮤텍스 — 상호 배제
mutex.lock()
count += 1 // 임계 구역: 한 스레드만
mutex.unlock()
공유 변수·자료구조를 "한 번에 한 스레드만" 만지게 한다. 잠근 쪽이 반드시 풀어야 하며, 안 풀면 데드락.
3. 세마포어 — 개수 제한
// 동시 3개까지만 DB 커넥션 사용
sem = Semaphore(3)
sem.acquire() // 자리 없으면 대기
queryDb()
sem.release() // 자리 반납
"동시에 N개까지만 허용"이 필요할 때 쓴다 — 커넥션 풀, 동시 다운로드 수 제한, 처리량 조절(스로틀링).
4. 언제 무엇을
- 공유 데이터 보호(한 번에 하나) → 뮤텍스
- 자원 개수 제한(N개 동시 허용) → 세마포어(카운팅)
- 이벤트 신호(생산자→소비자 알림) → 세마포어(바이너리)나 조건 변수
5. 함정
- 데드락 — 두 스레드가 서로의 락을 기다림. 락 획득 순서를 통일하면 예방
- 락 범위는 최소로 — 임계 구역이 길면 병목. 락 안에서 I/O·긴 작업 금지
- 고수준 언어는 보통 이 원시 도구 대신 스레드 안전 자료구조·채널·async를 권장한다
자주 묻는 질문
바이너리 세마포어는 뮤텍스와 같은 건가요?
동작은 비슷하지만(1개 허용) 소유권이 다릅니다. 뮤텍스는 잠근 스레드만 풀 수 있고, 세마포어는 다른 스레드가 release할 수 있습니다. 그래서 "보호"엔 뮤텍스, "신호"엔 세마포어가 맞습니다.
Node.js처럼 싱글 스레드면 뮤텍스가 필요 없나요?
JS 실행 자체는 단일 스레드라 메모리 경쟁은 없지만, await 사이에 다른 작업이 끼어들어 논리적 경쟁 상태가 생길 수 있습니다. 이땐 async 뮤텍스나 원자적 연산(DB 트랜잭션·멱등 처리)으로 제어합니다.
세마포어는 실무에서 어디에 쓰나요?
동시 실행 수 제한이 대표적입니다 — DB 커넥션 풀 크기, 외부 API 동시 호출 제한, 대량 작업의 병렬도 조절 등. "무한정 동시에 하면 터지는" 자원을 N으로 막습니다.

댓글 0