본문으로 바로가기

세마포어 — 동시에 N까지의 문지기

허가증 3장의 카운팅 세마포어에 스레드 5개가 몰리는 것을 실행한다 — 대기 큐, release가 곧 깨움, 그리고 뮤텍스와의 결정적 차이(소유권)까지.

1 / 7DB 커넥션이 3개뿐인데 스레드 5개가 동시에 쓰려 합니다. 세마포어 = '허가증 3장을 든 문지기'. acquire()로 한 장 받고, release()로 돌려줍니다.
허가증 3 / 3

카운팅 세마포어(3) — 자원 풀의 정원 관리

단계 목록 · 키보드 ←/→ 이동, Space 재생

세마포어는 '동시에 N까지'를 보장하는 카운터 문지기다 — 뮤텍스가 '한 명만, 잠근 자가 푼다'(상호 배제+소유권)라면, 세마포어는 '정원 관리와 신호 보내기'(소유권 없음)를 맡는다.

왜 헷갈리는가

"허가 1장짜리 세마포어 = 뮤텍스 아닌가?"가 고전 혼동이다. 동작은 비슷해 보여도 소유권이 다르다 — 뮤텍스는 잠근 스레드만 풀 수 있지만(우선순위 상속 같은 지원도 이 전제 위에), 세마포어는 아무나 release할 수 있다. 그래서 A가 끝나면 B를 깨우는 '신호'가 가능하고, 동시에 실수로 남의 허가를 풀어 버리는 사고도 가능하다.

애니메이션이 보여주는 것

허가 3장이 다 나가면 T4·T5는 잠든다 — 스핀(CPU를 태우는 바쁜 대기)이 아니라 재움이라는 것, release가 반납이자 깨움 신호라는 것이 동작의 핵심이다. 어느 순간에도 동시 사용자는 3을 넘지 않는다 — 처리량이 아니라 '동시성'의 상한이다.

서식지와 함정

세마포어가 사는 곳과 무너지는 곳.

  • 서식지: DB 커넥션 풀, 동시 다운로드/API 호출 제한(벌크헤드), 생산자-소비자의 빈칸/찬칸 카운터(고전 교과서의 bounded buffer).
  • 허가 누수: release를 잊으면(예외 경로!) 허가가 줄어든 채 영영 안 돌아온다 — try/finally·with·defer로 반납을 구조적으로 보장하는 것이 규율.
  • 세마포어도 데드락은 피해 가지 않는다 — 여러 세마포어를 교차 획득하면 같은 원이 생긴다.
  • 현대 언어의 화신들: Java Semaphore, Python asyncio.Semaphore, Go의 버퍼 채널(용량 N = 허가 N) — 이름이 달라도 같은 문지기다.

기억할 것

  • 세마포어 = 동시에 N까지. 뮤텍스 = 한 명만 + 소유권.
  • release = 반납이자 깨움 — 신호 보내기에 쓸 수 있는 이유(소유권 없음).
  • 허가 누수가 제1 사고 — 반납은 finally에.
  • 이진 세마포어 ≈ 뮤텍스처럼 보이지만, 소유권 부재가 용도와 위험을 가른다.
세마포어 — 동시에 N까지의 문지기 | KIE