두 축으로 갈립니다. 통과시키는 개수와 소유권입니다. 뮤텍스는 하나만 통과시키고 잠근 쪽만 풀 수 있고, 세마포어는 정해둔 개수만큼 통과시키며 누구나 올릴 수 있습니다.
뮤텍스 vs 세마포어
여러 스레드/프로세스가 공유 자원에 동시 접근하는 것을 제어하는 동기화 도구
뮤텍스 (Mutex)
[화장실 열쇠 1개]
Thread A: 열쇠 획득 (lock) 🔑
→ 화장실 사용 중 🚽
Thread B: 대기... ⏳
Thread A: 열쇠 반납 (unlock)
Thread B: 열쇠 획득 → 사용
상호 배제 (Mutual Exclusion)
열쇠 1개 = 한 번에 1개만 접근
소유권 있음 (잠근 스레드만 풀 수 있음)
세마포어 (Semaphore)
[주차장 빈 자리 3개]
count = 3
Car A 입장: count = 2 (P 연산)
Car B 입장: count = 1
Car C 입장: count = 0
Car D: 대기... (count = 0)
Car A 출차: count = 1 (V 연산)
Car D 입장: count = 0
카운팅 방식 (동시 N개 허용)
소유권 없음 (다른 스레드가 signal 가능)
핵심 차이
구분
뮤텍스
세마포어
동시 접근
1개만
N개까지
소유권
있음
없음
용도
상호 배제
리소스 카운팅
비유
열쇠 1개
주차장 카운터
값
0 또는 1
0 ~ N
이진 세마포어 vs 뮤텍스
이진 세마포어: count = 0 또는 1
뮤텍스: lock/unlock
비슷해 보이지만 다름!
- 뮤텍스: 소유권 (A가 잠그면 A만 풀 수 있음)
- 이진 세마포어: 소유권 없음
- 뮤텍스: 우선순위 역전 방지 가능
동기화 문제들
문제
설명
해결
경쟁 조건
동시 접근으로 결과 불일치
뮤텍스/세마포어
데드락
서로 잠금 대기
순서 통일, 타임아웃
기아
특정 스레드 영원히 대기
공정 스케줄링
우선순위 역전
낮은 우선순위가 잠금 보유
우선순위 상속
실무 활용
상황
도구
DB 커넥션 풀 (max 10)
세마포어(10)
파일 쓰기
뮤텍스
Rate Limiting
세마포어
싱글톤 초기화
뮤텍스
면접에서 이렇게 나옵니다
Q.뮤텍스와 세마포어의 차이를 설명해주세요.
소유권이 있는지가 핵심입니다.
항목
뮤텍스
세마포어
목적
상호 배제
자원 개수 관리와 신호 전달
값
잠김과 풀림
0 이상의 정수
소유권
있다. 잠근 스레드만 푼다
없다. 누구나 올릴 수 있다
동시 통과
하나
초기값만큼
소유권이 실무에서 중요한 이유는 실수를 잡아주기 때문입니다. 다른 스레드가 남의 잠금을 푸는 코드는 뮤텍스에서 오류로 드러나지만 세마포어에서는 조용히 통과해 두 스레드가 동시에 임계 구역에 들어갑니다.
세마포어는 상호 배제 말고도 쓸 수 있습니다. 한쪽이 신호를 올리고 다른 쪽이 기다리는 생산자 소비자 구조가 그것입니다. 뮤텍스로는 그 표현이 안 됩니다.
흔한 실수: 세마포어를 "여러 개 통과시키는 뮤텍스"로만 답하는 것. 소유권 유무와 신호 전달 용도가 더 본질적인 차이입니다.
Q.이진 세마포어와 뮤텍스의 차이는?
값의 범위는 같지만 소유권과 용도가 다릅니다.
항목
이진 세마포어
뮤텍스
값
0 또는 1
잠김과 풀림
잠근 주체만 해제
아니다
그렇다
초기값
0 으로 시작할 수 있다
항상 풀린 상태로 시작
재진입
없다
구현에 따라 지원한다
우선순위 역전 대응
없다
상속 기법을 지원하는 구현이 있다
초기값 0 이 가능하다는 점이 용도 차이를 만듭니다. 세마포어는 아직 아무도 안 가진 상태로 시작해, A 가 신호를 올릴 때까지 B 가 기다리는 순서 제어에 쓸 수 있습니다. 뮤텍스는 이 표현이 안 됩니다.
흔한 실수: 둘이 사실상 같다고 답하는 것. 상호 배제에는 뮤텍스를 쓰는 것이 원칙이고, 이유는 남이 푸는 실수를 막고 우선순위 역전 대응이 가능하기 때문입니다.
Q.경쟁 조건(Race Condition)이란 무엇인가요?
여러 실행 흐름이 공유 자원에 동시에 접근할 때, 실행 순서에 따라 결과가 달라지는 상태입니다.
스레드 A 는 잔액에 100을 더하고, B 는 50을 뺀다
순서
동작
잔액
1
A 가 읽는다
1000
2
B 가 읽는다
1000. 아직 A 가 쓰지 않았다
3
A 가 쓴다
1100
4
B 가 쓴다
950. A 의 변경이 사라졌다
원인은 읽고 계산해서 쓰는 것이 원자적이지 않기 때문입니다. 중간에 끼어들 틈이 있습니다.
해결
방법
잠금
임계 구역을 뮤텍스로 감싼다
원자적 연산
하드웨어가 보장하는 증감 연산을 쓴다
공유하지 않는다
스레드마다 따로 계산하고 마지막에 합친다
조건부 갱신
DB 라면 조건과 갱신을 한 문장으로
흔한 실수: 재현이 어려워 없다고 판단하는 것. 타이밍에 따라 드물게 나타나므로 테스트에서 안 보여도 운영에서 터집니다.
Q.실무에서 세마포어가 사용되는 예시를 들어주세요.
동시에 N개까지만 허용해야 하는 자리입니다.
예
초기값
DB 커넥션 풀
최대 연결 수
외부 API 호출 제한
초당 허용 호출 수
파일 다운로드 동시 실행 수
대역폭을 고려한 값
무거운 작업의 동시 실행 제한
코어 수 안팎
생산자 소비자 큐
빈 자리 수와 채워진 수 각각
마지막은 상호 배제가 아니라 신호 전달입니다. 큐가 비었으면 소비자가 기다리고, 생산자가 넣으면 신호를 올려 깨웁니다.
주의할 것은 반납입니다. 예외가 난 경로에서 반납을 빼먹으면 카운트가 한 방향으로만 줄어 결국 모두 멈춥니다. 획득과 반납을 자원 관리 구문으로 묶어야 합니다.
흔한 실수: 세마포어로 상호 배제를 구현하는 것. 동작은 하지만 소유권이 없어 남이 푸는 실수를 못 막습니다. 하나만 통과시켜야 하면 뮤텍스입니다.