[iOS] DispatchGroup

kyuchulkim
5 min readAug 17, 2023

--

A group of tasks that you monitor as a single unit.

작업을 Queue에 보내게 되면 GCD가 스레드를 적절히 생성해서 분배해준다. 하지만 여러 스레드에 퍼진 작업들이 한번에 끝나는 시점은 알 수 없다.

이 문제를 해결할 때 DispatchGroup을 사용한다.

말로 들었을 땐 잘 모르겠지만, 네트워크 통신을 할 때 자주 발생하는 문제를 해결 할 수 있다.🦿

networkService.postDetectLanguage(query: text ?? "") { result in
switch result {
case .success(let code):
self.networkService.postTranslateLanguage(langCode: code, text: text ?? "") { result in
switch result {
case .success(let data):
self.translationTextview.text = data
case .failure(let error):
print(error.errorString)
}
}
case .failure(let error):
print(error.errorString)
}
}
  • 코드를 보면 networkService.postDetectLanguage 네트워크 클로저 안에서 networkService.postTranslateLanguage 를 한번 더 호출하고 있는 상태이다.
  • DispatchGroup을 이용하면 이러한 콜백지옥을 벗어날 수 있다.

DispatchGroup

let myGroup = DispatchGroup()

  • 그룹 선언

func enter()

  • 작업이 그룹에 들어감을 명시해준다.
  • task reference count +1.

func leave()

  • 작업이 그룹에서 나감을 명시해준다.
  • task reference count -1

func notify(qos: DispatchQoS, flags: DispatchWorkItemFlags, queue: DispatchQueue, execute: () -> Void)

  • 현재 그룹의 모든 작업 실행이 완료되면(enter: 0) execute 후행 클로저에서 모든 작업이 완료된 후 일시적으로 처리해야할 작업을 실행시킬 수 있다.
func callAllRecommendation() {
let group = DispatchGroup() // DispatchGroup 생성

// enter (Task + 1)
group.enter()
callRecommendation(id: 976573) { data in
self.list = data
group.leave() // leave (Task - 1)
}

// enter (Task + 1)
group.enter()
callRecommendation(id: 479718) { data in
self.secondList = data
group.leave() // leave (Task - 1)
}

// enter (Task + 1)
group.enter()
callRecommendation(id: 569094) { data in
self.thirdList = data
group.leave() // leave (Task - 1)
}

// enter (Task + 1)
group.enter()
callRecommendation(id: 565770) { data in
self.fourList = data
group.leave() // leave (Task - 1)
}

// Task == 0
group.notify(queue: .main) {
self.posterCollectionView.reloadData()
}
}

// 네트워킹
func callRecommendation(id: Int, compleationHandler: @escaping (Recommendation) -> Void) {
AF.request(url, method: .get).validate().responseDecodable(of: Recommendation.self) { response in
... 네트워킹
  • Alamofire을 활용한 네트워킹 메서드는 비동기적 메서드임으로 현재 쓰레드가 아닌 다른 쓰레드에 각각 처리된다.
  • callAllRecommendation 메서드는 callRecommendation 메서드를 4번 실행 시키고 있는데, 각각 callRecommendation은 실행되기 전 group.enter()을 통해 Task Count를 증가시키고 group.leave()로 이스케이핑 클로저에서 메서드가 끝날 때 Task Count를 감소 시키고 있다.
  • 즉, Task가 모두 실행되고 끝나는 시점 (Task == 0)에 notify 를 실행시켜 모든 데이터를 받아온 후 한번에 CollectionView를 reload 할 수 있었다.

간단하게 DispatchGroup와 DispatchSemaphore의 차이

DispatchGroup

  • 작업에 추가된 모든 작업들이 한번에 끝나는 시점을 알 수 있다. (notify)
  • 작업의 순서는 알 수 없다. 그저 그룹 내의 모든 작업이 한번에 끝나는 시점을 알 수 있음

DispatchSemaphore

  • 한번에 수행될 수 있는 작업의 수를 제한 할 수 있다.
  • let semaphore = DispatchSemaphore(value: 3) ← 3개로 제한
  • wait과, signal을 통해 Task의 순서를 정할 수 있는 거 같다.

DispatchSemaphore는 공부가 더 필요👹

--

--