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

Dispatch Group의 개념에 대해 알아봅시다

Previously on GCD…

저번 시간에는 GCD 사용시 주의 사항에 대해 살펴보았는데요, 오늘은 Dispatch Group이라는 새로운 개념을 알아보도록 합시다.

Dispatch Group의 목적

우리는 작업(task)을 Dispatch Queue에 보내면, GCD가 스레드를 적절히 생성해서 분배해준다는 사실을 알고 있어요. (모른다면 이전 시리즈들을 정주행 하고 오는걸 추천합니다!)

이때 여러 스레드로 분배된 작업들이 끝나는 시점을 각각 파악하는 것이 아니라, 하나로 그룹지어서 한번에 파악하고 싶을때 Dispatch Group의 개념이 사용됩니다.

즉, 그룹으로 묶인 작업의 마지막 시점을 파악하는 것이죠!

실제 상황을 예를 들면 모든 이미지 다운로드가 완료된 시점에 특정 행동을 해야한다면 해당 시점을 캐치하기 위해 Dispatch Group의 개념을 사용할 수 있습니다.

Dispatch Group 사용 방법

디스패치 그룹에 대한 개념을 알아봤으니, 이제 어떻게 사용하면 되는지 코드로 봅시다.

1. DispatchGroup 생성

let group = DispatchGroup()

2. async 메소드로 task를 보낼때, group 파라미터에 생성한 그룹 추가

DispatchQueue.global().async(group: group1) { // task }

큐로 보낼때 어떤 그룹에 넣을건지 정해주는 것입니다.

이때 task 를 다른 큐로 보내더라도 같은 그룹으로 지정할 수 있습니다.

DispatchQueue.global(qos: .utility).async(group: group1) { // task }
DispatchQueue.global().async(group: group1) { // task }

3. notify(queue:) 를 통해 notification block(그룹으로 묶인 모든 작업이 끝났을때 실행될 작업)을 넘김

//main queue 에서 notification block 을 실행한다 
group1.notify(queue: .main) { [weak self] in
self?.myLabel.text = "Group1으로 묶인 모든 작업이 끝났습니다."
}

이때 queue 파라미터는 해당 작업을 어느 곳에서 실행할지를 정합니다.

만약 group1 로 묶인 모든 task가 다 끝났거나, 아예 들어오지 않아 비어있는 상태라면 notification block은 바로 실행됩니다.

이렇게 작업이 끝나는 시점을 notify로 받는 방법 이외에도, DispatchGroup의 wait() 함수를 통해, 해당 그룹의 모든 작업이 완료때까지 현재 스레드를 block 시킬 수 있습니다.

let group1 = DispatchGroup()
DispatchQueue.global().async(group: group1) { }
DispatchQueue.global().async(group: group1) { }
DispatchQueue.global(qos: .utility).async(group: group1) { }
group1.wait()

그룹 작업이 다 끝나야만 다음 작업을 할 수 있는 상황에서, 어떤 이유로 그룹의 완료 알림에 비동기적으로 응답할 수 없는 경우에 해당 메소드를 이용하면 됩니다. (그런데 사실 여기에 해당하는 경우는.. 많이 없는 것 같아요..?🤔)

그룹이 완료되길 무한정으로 기다리지 않도록, group1.wait(timeout:) 처럼 timeout 파라미터를 통해 얼마나 기다릴지에 대한 시간을 지정할 수 있습니다. 이를 통해 일정 시간 이후에는 다음 작업이 마저 진행됩니다. (그렇다고 시간내에 안끝난 작업을 멈추는건 아니고, 다른 스레드에서 계속 진행)

wait(timeout:)DispatchTimeoutResult 를 반환하는데 이 값을 통해 시간 내에 그룹 내 모든 task 가 완료되었는지 판단할 수도 있습니다.

let timeoutResult = group1.wait(timeout: .now() + 60)switch timeoutResult {
case .success:
print("60초 안에 그룹 내 모든 task 끝냄")
case .timedOut:
print("60초 안에 그룹 내 모든 task 못끝냄")
}

🔥 wait() 함수 사용시 주의 사항 🔥

wait() 함수를 실행하는 곳(not 메인 큐)과 task를 보내는 큐 (not 현재 큐) 유의하자

위처럼 group1.wait() 를 통해 DispatchGroup에 대해 wait 를 걸게 되는데, 여기서 주의할 점은 이 함수를 실행하는 곳이 메인 큐 (메인 스레드)면 안됩니다. 왜냐하면 wait함수를 호출한 현재의 스레드를 블럭하기 때문에, 메인 스레드에서 실행하면 메인 스레드가 멈추는, 즉 task들이 다른 스레드에서 실행되는 시간만큼 앱이 멈추는 상황이 발생하게 됩니다.

또한 그룹 내의 taskwait가 실행되고 있는 현재의 스레드로 할당이 되어선 안됩니다. 왜냐면 wait 를 실행하고 있는 스레드는 멈춰있을텐데 그곳으로 task 가 할당된다면 데드락 상황이 발생할테니까요. 따라서 현재의 스레드로 task를 할당 할 가능성이 있는 큐, 즉 현재 큐(현재 스레드에 wait 을 실행하도록 할당한)로 task 를 보내면 안됩니다. (데드락…? 뭐라고..??? 하는 생각이 드시는 분은 여기에서 GCD에서 발생할 수 있는 데드락의 상황을 읽고 옵시다)

마무리

ㅎ ㅏ.. 요즘 너무 공부를 안해서 걱정이네요.. 1월 달에 들은 내용들 다 정리할 수 있기를 ㅠ

정말정말정말 도움 많이 되는 강의입니다 ㅠ 돈이 아깝지 않으니…. 절 대 수 강 해

그럼 저는 블로그 쓰는(aka. ppt 만드는) 짤과 함께 사라지도록 하겠습니다. 그럼 20000!

이전 포스팅 👈🏻

다음 포스팅 👉🏻

출처

 https://github.com/sujinnaljin/TIL