iOS/Swift

Swift ;; GCD #2 Async와 Sync

may_wonhui 2023. 3. 14. 14:03

앞서 GCD는 DispatchQueue를 사용하여 작업을 분산한다고 했다.

https://may1coding.tistory.com/55

 

Swift ;; GCD(Grand Central Dispatch) #1 정의

GCD는 동시성 프로그래밍을 위해 Apple이 제공하는 API이다. (iOS 8부터 지원) GCD는 DispatchQueue를 이용하여 작업을 분산처리한다. 개발자가 스레드를 생성하는 작업 없이 DispatchQueue라고 이름 붙여진 Qu

may1coding.tistory.com

 

우리(개발자)가 작업을 분산 처리하고 싶을 때,

DispatchQueue로 작업을 보내기만 하면, 

큐가 알아서 특성에 따라 스레드로 분산 처리를 해준다.

 

 

 

최소한 개발자가 지정해줘야할 것은

1. 작업(task)을 async로 보낼 것인지, sync로 보낼 것인지

2. Serial Queue로 보낼 것인지 Concurrent Queue로 보낼 것인지 이다.

 

 


먼저, async와 sync에 대해 알아보려고 한다.

 

iOS 앱 개발하면서 엄청나게 많이 접한, or 접하게 될(?) 코드!

DispatchQueue.main.async {
    //Task 단위
}

UI 업데이트 코드는 반드시 위의 코드 내부에 작성해주어야 한다.

 

 

 


Async (안 기다린다)

그래서 async는 무슨 의미일까?

안 기다린다??

 

개발자가 메인 스레드에서

DispatchQueue로 작업을 async(비동기)로 보내면,

그 작업이 완료되지 않아도 기다리지 않고 다른 작업을 곧바로 시작할 수 있는 상태가 된다는 의미이다.

 

 

기본적으로 개발자가 코드를 작성하면, 그것은 메인 스레드에서 작동한다.

 

 

1. 개발자(메인 스레드)가 Task1, Task2, Task3을 분산처리 하려고 한다.

 

2. 개발자(메인 스레드)가 Task1을 Async로 보냈다.

 

3. 개발자(메인 스레드)는 Task1이 완료되지 않아도

작업을 보낸 즉시 결과를 기다리지 않고 다음 작업을 진행할 수 있다.

 


Async 실습

print("Task1 dispatch to queue")
DispatchQueue.global().async {
    sleep(1)
    print("1 🔚")
}

print("Task2 dispatch to queue")
DispatchQueue.global().async {
    sleep(3)
    print("2 🔚")
}

print("Task3 dispatch to queue")
DispatchQueue.global().async {
    sleep(2)
    print("3 🔚")
}

 

출력)

Task1 dispatch to queue
Task2 dispatch to queue
Task3 dispatch to queue
1 🔚
3 🔚
2 🔚

 

총 소요 시간)

0.00291895866394043

 

설명) 출력 결과가 이해가지 않으면 아래 더보기 클릭!

더보기

메인 스레드에서

print("Task1 dispatch to queue")

1. "Task1 dispatch to queue" 출력

 

이후 async로 1초 후 "1 🔚" 을 출력하는 Task를 DispatchQueue로 보냈다.

DispatchQueue.global().async {
    sleep(1)
    print("1 🔚")
}

async로 보냈으므로, 메인 스레드는 Task를 기다리지 않고 곧바로 다른 작업을 할 수 있는 상태가 된다.

 

위의 작업이 완료("1 🔚" 을 출력)되지 않았음에도 메인 스레드는 작업을 수행한다.
print("Task2 dispatch to queue")

2. "Task2 dispatch to queue" 출력

 

이후 async로 3초 후 "2 🔚" 을 출력하는 Task를 DispatchQueue로 보냈다.
DispatchQueue.global().async {
    sleep(3)
    print("2 🔚")
}

마찬가지로, async로 보냈으므로, 메인 스레드는 Task를 기다리지 않고 곧바로 다른 작업을 할 수 있는 상태가 된다.

 

메인 스레드는 곧바로 다음 작업을 수행한다.

print("Task3 dispatch to queue")

3. "Task3 dispatch to queue" 출력

 

이후 async로 2초 후 "3 🔚" 을 출력하는 Task를 DispatchQueue로 보냈다.
DispatchQueue.global().async {
    sleep(2)
    print("3 🔚")
}

 

1초 후,

4. "1 🔚" 출력

 

2초 후,

5. "3 🔚" 출력

 

3초 후,

6. "2 🔚" 출력


Sync (기다린다)

Async와 반대되는 개념인 sync.

안 기다리는 Async와 달리 Sync는 기다린다.

 

Async는 작업을 보내고 작업이 완료될 때까지 기다리지 않고, 메인 스레드가 다른 작업을 처리할 수 있는 상태가 된다고 했다.

Sync는 작업을 보내면 작업이 완료될 때까지 기다린다.

따라서 해당 작업이 완료될 때까지 메인 스레드는 블락되어 다른 작업을 처리할 수 없다.

sync로 보낸 작업이 완료된 이후에 메인 스레드는 다른 작업을 처리할 수 있는 상태가 된다.

 

 

1. 개발자(메인 스레드)가 Task1, Task2, Task3을 분산처리 하려고 한다.

 

2. 개발자(메인 스레드)가 Task1을 Sync로 보냈다.

 

3. 개발자(메인 스레드)는 Task1이 완료될 때 까지 아무 작업도 진행할 수 없이 기다린다.

Task1이 완료된 이후에 다음 작업을 진행할 수 있다.

 

Task1이 1초, Task2가 3초, Task3이 2초 걸리는 작업이라고 해보자.

 

메인 스레드는 Task1을 DispatchQueue로 보내면 Task1은 sync이기 때문에

메인 스레드는 Task1이 완료될 때까지 블락되어 다른 작업을 하지 못하고 기다린다. (1초)

 

Task1이 완료되면 Task2를 queue로 보내는데 이 역시 sync이기 때문에

메인 스레드는 Task2가 완료될 때까지 기다린다. (2초)

 

Task2가 완료되면 Task3까지 완료 (3초)

총 6초가 걸린다.

 

**추가 (수정필요)

메인 스레드에서 sync 작업을 글로벌 큐로 보내서 처리하는 것과

메인 스레드에서 Task1, Task2, Task3을 순차적으로 처리한다고 했을 때와 같아보이는데??

 

그렇다.

sync를 사용하면 global큐(메인 스레드 X)에 작업을 보내는 코드를 작성하더라도

실질적으로는 메인 스레드(작업을 보내는 스레드)에서 작업한다.

 

메인 스레드에서 sync task를 글로벌 큐로 보내도

해당 작업이 완료될 때까지 메인 스레드는 기다리기 때문이다.

 

따라서 만일 메인스레드에서 시간이 오래 걸리는 작업을 sync로 보내게 된다면

화면이 멈추거나 버벅이는 현상이 발생할 수 있다.


Sync 실습

print("Task1 dispatch to queue")
DispatchQueue.global().sync {
    sleep(1)
    print("1 🔚")
}

print("Task2 dispatch to queue")
DispatchQueue.global().sync {
    sleep(3)
    print("2 🔚")
}

print("Task3 dispatch to queue")
DispatchQueue.global().sync {
    sleep(2)
    print("3 🔚")
}

 

출력)

Task1 dispatch to queue
1 🔚
Task2 dispatch to queue
2 🔚
Task3 dispatch to queue
3 🔚

 

총 소요 시간)

6.004613041877747