[iOS] 차근차근 시작하는 GCD — 10

DispatchSemaphore 에 대해 알아봅시다

naljin
8 min readFeb 8, 2021

Previously on GCD…

개-하! 저번 시간에는 작업을 class 로 만들어서 취소 / 순서 기능을 사용할 수 있는 DispatchWorkItem에 대해 알아봤는데요!

오늘은 한번에 수행할 수 있는 작업의 수를 제한하는 DispatchSemaphore에 대해 알아보도록 합시다. 가벼운 내용이니까 가벼운 맘으로 고고! 라고 쓰고 시작했는데 쓰다보니 무거워진거 같아요.. 족굼은 마음의 준비를 하고 내리시길.. ㅎㅎ

Semaphore

여러분 Semaphore 에 대해 아시나여? 위키의 정의를 잠깐 가져와봅시다.

정수 변수로서, 멀티프로그래밍 환경에서 공유 자원에 대한 접근을 제한하는 방법으로 사용된다.

스레드가 공유 자원의 배타적인 사용을 보장받기 위해서 임계 구역에 들어가거나 나올때는 세마포어 같은 동기화 매커니즘이 사용된다.

흠.. 가릿? 여기서 공유 자원이 뭐니.. 작업을 왜 제한해야하느니.. 등은 설명하지 않을거예요! 세마포어에 대해 설명해놓은 글들은 많으니까요!!

이 글에서는 “Semaphore란 공유 자원에 접근하는 작업의 수를 제한할 때 사용한다” 의 배경 지식을 바탕으로, iOS 에서는 어떻게 세마포어를 사용하는지 알아볼 것입니다.

오랜만에 운영체제 수업 ppt 도 봤는데 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이해를 못해서 의문 가득한 표정 그려놨었네요 ㅋㅋㅋㅋㅋㅋㅋㅋ

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ우측 하단의 ??ㅇ0ㅇ??

DispatchSemaphore

1. 동시 작업 개수 제한

iOS 에서는 세마포어를 위해 DispatchSemaphore 라는 객체를 이용합니다.

// 공유 자원에 접근 가능한 작업 수를 2개로 제한
let semaphore = DispatchSemaphore(value: 2)

위와 같이 공유 자원에 접근 가능한 (혹은 한번에 실행 가능한) 작업 수를 명시하고, 임계 구역에 들어갈때는 semaphore의 wait()를, 나올때는 signal() 을 호출합니다.

for i in 1...3 {
semaphore.wait() //semaphore 감소
DispatchQueue.global().async() {
//임계 구역(critical section) print("공유 자원 접근 시작 \(i) 🌹")
sleep(3)
print("공유 자원 접근 종료 \(i) 🥀")
semaphore.signal() //semaphore 증가
}
}

wait()는 “나 들어갈테니까 기다려!” 하면서 semaphore를 감소시키고, signal()은 “나 끝났으니까 신호준다!”하면서 semaphore를 증가시키는 것으로 이해할 수 있습니다.

이렇게 처음 semephore를 초기화할때 전달한 value (여기서는 2) 안에서 작업의 개수가 왔다갔다 하는거죠!

공유 자원 접근 시작 1 🌹
공유 자원 접근 시작 2 🌹
공유 자원 접근 종료 2 🥀
공유 자원 접근 종료 1 🥀
공유 자원 접근 시작 3 🌹
공유 자원 접근 종료 3 🥀

2. 두 스레드의 특정 이벤트 완료 상태 동기화

사실 DispatchSemaphore은 두가지 방식으로 사용할 수 있는데, 하나는 위에서 살펴봤듯이 동시 작업 개수를 제한하는 것입니다.

다른 하나는 두 스레드가 특정 이벤트의 완료 상태를 동기화 하는 경우에 유용하다는 것입니다.(useful for when two threads need to reconcile the completion of a particular event.)

이게 뭔말이냐면!!

  • 스레드 A는 작업 A 실행 중
  • 스레드 B는 작업 A가 끝난 후에 무언가를 실행하려고함

이 상황에서 스레드 B(소비자)는 예상된 작업을 기다리기 위해 wait를 호출하고, 스레드 A(생성자)는 작업이 준비되면 signal를 호출하는 식으로 스레드 B가 작업 A 의 완료 상태를 동기화할 수 있다는 거죠!!

DispatchSemaphore를 해당 용도로 사용할때는 초기값을 0으로 설정합니다.

😡 아 먼소리냐~~~ 코드 내놔라~~!!!

//DispatchSemaphore 초기값 0으로 설정
let semaphore = DispatchSemaphore(value: 0)
print("task A가 끝나길 기다림")// 다른 스레드에서 task A 실행
DispatchQueue.global(qos: .background).async {
//task A
print("task A 시작!")
print("task A 진행중..")
print("task A 끝!")
//task A 끝났다고 알려줌
semaphore.signal()
}
// task A 끝날때까지는 value 가 0이라, task A 종료까지 block
semaphore.wait()
print("task A 완료됨")

task A가 끝나지 않았다면 (즉 signal() 이 실행되지 않았다면) semaphore.wait() 이후의 작업은 실행되지 않을거예요. 왜냐면 그전까지 세마포어 값은 0일테니까요!

이제 “두 스레드가 특정 이벤트의 완료 상태를 동기화” 할때 어떻게 세마포어를 이용하는지 이해가 되시나요?!

마무리

iOS 에서는 Semaphore의 역할을 하는 DispatchSemaphore 가 있고, signal()wait() 를 통해 값을 증가/감소 시키면서 특정 영역에 접근 가능한 작업의 수를 제한시킨다. 정도로만 가져가도 괜찮을 것 같아요!

그럼.. 또 돌아오도록 하겠습니다 ㅎㅎ 그럼 20000…

이전 포스팅 👈🏻

다음 포스팅 👉🏻

출처

--

--