let sem =DispatchSemaphore(value: 3) // 동시 3개 제한 for req in requests { DispatchQueue.global().async { sem.wait() self.performRequest(req) { result in defer { sem.signal() } print("Result:", result) } } }
네트워크 품질에 민감한 환경(모바일)에서 실패율을 크게 낮출 수 있는 조합!
4 임계 구역 보호 (간단 뮤텍스처럼)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
finalclassSafeBox<T> { privatevar value: T privatelet sem =DispatchSemaphore(value: 1)
init(_v: T) { self.value = v }
funcread() -> T { sem.wait(); defer { sem.signal() } return value }
funcwrite(_newValue: T) { sem.wait(); value = newValue; sem.signal() } }
설정 팁: URLSession과 함께 쓰면 좋은 세팅
1 2 3 4 5 6 7
let config =URLSessionConfiguration.default config.waitsForConnectivity =true// 연결 복구 시 자동 재개 config.timeoutIntervalForRequest =30 config.timeoutIntervalForResource =60 config.httpMaximumConnectionsPerHost =3// 호스트당 동시 연결 수 제한 config.requestCachePolicy = .reloadIgnoringLocalCacheData let session =URLSession(configuration: config)
waitsForConnectivity = true는 일시 연결 끊김 시 자동 대기 후 재시도해 실패를 줄일 수 있다.
주의할 점 & 안티 패턴
메인 스레드에서**wait()**금지 → UI 프리즈/데드락 위험
항상**signal()**보장 (defer { signal() } 권장)
같은 직렬 큐 안에서 다시**wait()**하면 데드락 가능
무한 대기 방지: 필요시 wait(timeout:) 사용
1 2 3
if sem.wait(timeout: .now() +5) == .timedOut { print("⏱️ timeout") }
공정성(FIFO) 보장 안 됨 → “순서 보장”이 절대적이면 Serial Queue가 더 적합
복잡한 워크플로 제어에는 OperationQueue가 더 가독성 좋을 수 있음
iOS 15+: 가능하면 async/await + AsyncSemaphore(직접 구현) 또는 OperationQueue 고려
다른 도구와 비교
도구
목적
특징
DispatchSemaphore
동시성 제한/동기화
블로킹, 가볍고 단순, 신중히 사용 필요
DispatchGroup
여러 비동기 작업 완료 대기
동시성 제한 기능 없음
NSLock
임계 구역 보호
뮤텍스, 카운팅/슬롯 개념 없음
OperationQueue
작업 스케줄링/의존성/취소
maxConcurrentOperationCount로 제한, 가독성 좋음
Serial DispatchQueue
순차 실행
가장 단순한 순서 보장, 동시성 제한(>1) 불가
async/await
구조적 동시성
iOS 15+, 가독성 최고, 블로킹 없음
요약
DispatchSemaphore는 간단한 코드로 동시성 제어를 제공하며, 특히 대량 네트워크 요청의 실패율을 낮추는 데 매우 효과적