← 기술 지식

기술 지식 / 컴퓨터 과학 / 동시성 / 동기화

세마포어 허가의 획득·취소·반환

상호 배제와 동시 실행 제한을 나누고 acquire 전후 취소 경쟁·단일 반환·실제 작업 종료와 대기 상한을 설명합니다.

동시성 · 원리·구현·운영
기술 지식 트리

세마포어 동시성 제한과 뮤텍스의 공유 상태 보호

외부 API 호출을 동시에 최대 10개만 허용하려면 허가 10개를 가진 세마포어를 사용할 수 있습니다. 공유 목록의 불변식을 한 번에 한 스레드만 바꾸게 하려면 뮤텍스가 맞습니다. 세마포어 안에 10개 작업이 들어갈 수 있다는 것은 그 작업들의 데이터 접근이 서로 안전하다는 뜻이 아닙니다.

이진 세마포어도 뮤텍스와 항상 같은 계약은 아닙니다. 소유 스레드만 unlock해야 하는지, 우선순위 역전 처리·재귀·공정성 지원이 있는지 구체 API를 확인해야 합니다.

허가 소유와 반환 책임

상황 허가 소유 할 일
acquire 전에 취소 없음 대기 등록 제거, release 금지
acquire 성공 직후 취소 있음 작업 미시작이면 한 번 반환
실행 중 사용자 timeout 있음 실제 종료까지 유지하거나 책임 이전
실행 실패·정상 완료 있음 공통 종결 경로에서 한 번 반환

획득 실패도 finally에서 무조건 release하면 허가가 11개로 늘어 상한이 깨집니다. 성공 경로에만 release하면 예외에서 누수되어 나중 요청이 영원히 기다립니다.

취소 시점에 따라 반환 책임이 달라집니다허가 대기: 아직 release 책임 없음. 획득 성공: 정확히 한 번 반환할 책임. 실제 작업 실행. 실제 종결 후 반환acquire 확정시작 허용성공·실패·취소 완료허가 대기아직 release 책임 없음획득 성공정확히 한 번 반환할 책임실제 작업 실행실제 종결 후 반환
취소 시점에 따라 반환 책임이 달라집니다 화살표는 허가 수명의 순서입니다. acquire가 성공한 뒤부터 반환 책임이 생기며, 응답 종료가 아니라 제한 대상인 실제 작업 종료까지 유지합니다.
그림을 글로 읽기
  • 허가 대기: 아직 release 책임 없음
  • 획득 성공: 정확히 한 번 반환할 책임
  • 실제 작업 실행
  • 실제 종결 후 반환
  1. 허가 대기 → 획득 성공: acquire 확정
  2. 획득 성공 → 실제 작업 실행: 시작 허용
  3. 실제 작업 실행 → 실제 종결 후 반환: 성공·실패·취소 완료

세마포어의 숫자는 동시에 허용할 작업 수를 나타내지만, 그 숫자가 작업의 소유권이나 종료를 자동으로 관리하지는 않습니다. 따라서 허가를 받은 순간부터 실제 작업이 끝나 반환되는 순간까지를 하나의 수명으로 추적해야 상한과 누수를 함께 설명할 수 있습니다.

상태를 available=2, active={}로 시작해 두 요청 A·B가 각각 허가를 얻으면 available=0, active={A,B}가 됩니다. C의 acquire가 대기 중일 때 A의 사용자 응답이 먼저 끝나도 실제 외부 호출이 남아 있으면 A의 허가를 반환하지 않습니다. A의 외부 호출이 종결된 뒤에만 active={B}로 바꾸고 허가 하나를 C에게 넘깁니다. 이 순서를 테스트에서는 acquire 직전 취소, acquire 성공 직후 취소, 작업 제출 실패, 실제 작업이 취소를 무시하는 경우로 나누어 관찰하고 각 경우의 허가 수와 active 작업 집합을 함께 비교합니다.

허가 객체와 소유권 경계

permit = acquire_or_cancel(deadline)
if permit was not acquired:
    return cancelled_or_timeout
try:
    if cancellation_requested:
        return cancelled_before_start
    await actual_operation_termination()
finally:
    permit.release_once()

이 슈도코드는 await가 사용자 대기 timeout만 반환하고 실제 I/O를 남기는 것이 아니라 실제 작업 종결을 기다린다는 전제입니다. 사용자 응답을 먼저 끝내야 한다면 permit을 작업 소유자에게 넘기고 그 소유자가 종료 시 반환해야 합니다. RAII·finally도 보호 범위를 실제 작업보다 짧게 잡으면 상한을 깨뜨립니다.

acquire와 취소가 동시에 완료되면, 호출자에게 성공한 permit이 도착한 경우에는 작업을 시작하지 않더라도 호출자가 한 번 반환해야 합니다. 반대로 API가 취소·timeout으로 종료되며 permit을 내부에서 되돌린 계약이라면 호출자는 permit을 받은 것으로 처리해 release하지 않아야 합니다. 두 경로가 같은 허가를 각각 반환하지 않도록 성공·취소 결과의 소유자를 하나로 정합니다.

대기자 큐와 메모리·기한 상한

활성 작업 10개를 제한해도 허가를 기다리는 요청 100만 개를 만들면 메모리·연결·기한이 고갈됩니다. acquire 대기자 수·바이트·최대 나이에도 상한을 둡니다. 취소된 waiter가 큐에 남아 나중 허가를 소비하지 않도록 정리합니다.

FIFO 세마포어를 쓰면 공정성의 일부를 얻을 수 있지만 우선순위·큰 작업·취소 처리 비용이 남습니다. 기본 세마포어가 요청 도착 순서나 모든 작업의 완료 기한을 보장한다고 가정하지 않습니다.

허가 단위와 실제 작업 비용

작은 조회와 큰 export에 각각 1개씩 주면 동시 개수는 같아도 메모리·I/O 비용은 크게 다릅니다. 가중 허가나 별도 풀을 고려하고, 여러 인스턴스의 합계가 하위 서비스 예산을 넘지 않게 합니다. 실행 수 제한과 초당 요청률 제한도 다른 제약입니다.

검증은 허가 전 취소·획득 직후 취소·제출 실패·중복 완료·취소 무시 작업을 순서대로 주입합니다. 항상 사용 가능 + 소유 중 = 초기 허가인지와 실제 active 수가 상한을 넘지 않는지 대조합니다. 카운터 숫자뿐 아니라 그 허가가 어떤 작업 수명을 대표하는지 확인해야 합니다.

후속·관련 개념

함께 읽을 문서

전체 기술 지식
선택 연습 · 관련 질문 2개

질문은 이해를 확인하는 보조 자료입니다. 학습 경로와 문서의 범위는 질문 수와 독립적입니다.