안녕하세요, 루피입니다.
오늘은 저번 시간에 이어 DispatchQueue에 대해 공부해 보도록 하겠습니다. 만약에 이해가 잘 가지 않는다면, 저번 글 을참고하시면 좋을 거 같습니다! 바로 시작하겠습니다.
DispatchQueue란?
DispatchQueue가 무엇인지에 대해 작성하기에 앞서 저번 글에 있던 내용을 상기시켜 보도록 하겠습니다.
GCD(Grand Central Dispatch)는 멀티코어 하드웨어의 자원을 효율적으로 활용할 수 있게 도와주는 시스템 수준의 기술입니다.
간단하게 말해, GCD를 사용하면 여러 코어에 걸쳐 작업을 병렬 처리할 수 있습니다.
우리는 GCD 덕분에 Thread 관리나 스케줄링에 대한 고민을 줄이고 DispatchQueue에 작업을 할당하면 GCD가 알아서 동시성 프로그래밍을 실행해 준다는 사실을 알고 있습니다.
즉, DispatchQueue란 앱의 메인 Thread나 백그라운드 Thread에 작업을 할당하고 실행 순서를 관리하는 객체(조금 더 정확하게 말하면 Queue)입니다.
Queue 종류
Thread에 Task 부여 방식에 따른 차이입니다.
Serial Queue
우리 말로는 직렬, 순차적이라고 이해하면 될 거 같습니다.
Serial은 Task를 다른 하나의 Thread에서 순서대로 처리하게 만들어 줍니다.
(다른 하나의 Thread에서 순서대로 처리하는게 포인트가 아니기에 수정합니다.)
Serial Queue는 작업을 순차적으로, 즉 한 번에 하나씩만 처리하며, 작업 간 순서를 보장합니다. 그렇기에 우리는 대략적인 코드의 실행 순서를 파악할 수 있는 장점이 있습니다.
Concurrent Queue
우리 말로는 동시적이라고 이해하면 될 거 같습니다.
직렬과 다르게 단일 Thread에서 처리하게 만들어 주는 것이 아닌 다수의 Thread에서 작업을 처리하게 도와줍니다.
Concurrent는 작업을 다수의 Thread에서 처리하게 만들어 줍니다. 그렇기에 실행 순서를 파악하기에는 어렵지만, 빠른 속도라는 장점이 있습니다.
Concurrent Queue는 작업을 순차적으로 하나씩 처리하는 것이 아닌 동시에 여러 작업을 처리할 수 있습니다.
Concurrent Queue는 여러 Thread에서 작업을 할 수 있는 능력이 있지만, 매번 여러 Thread에 작업을 할당하는 것은 아닙니다. 할당의 경우 GCD가 상황에 동적으로 할당합니다. 그렇기에 우리는 실행 순서를 파악하기에는 어렵지만, 빠른 속도와 효율적으로 작업을 처리할 수 있다는 장점이 있습니다.
Task 처리 방법
Sync
우리 말로는 동기라고 이해하면 됩니다.
Sync 방식은 작업을 등록하고 기다리다가 응답이 오면 그다음 작업을 진행하는 방식입니다. 좀 더 자세하게는 Sync는 호출 Thread에서 즉시 작업을 실행하고, 완료될 때까지 다음 작업을 진행하지 않습니다. 하지만, 이러한 방식은 UI 응답성을 저하시킬 수 있으므로 주의가 필요합니다.
예를 들어...
친구에게 전화를 걸어 대화하는 상황을 가정해 보겠습니다. 내가 말을 하고 상대방의 답변을 기다리는 동안, 대화가 완전히 끝날 때까지 다른 일을 하지 않습니다. 즉, 한 작업이 끝날 때까지 기다리는(블록되는) 모습이 동기적 처리와 유사합니다.
Async
우리 말로는 비동기라고 이해하면 됩니다.
Async 방식은 작업을 등록하고 응답이 올때 까지 기다리는 게 아니라 다른 작업을 먼저 진행하는 방식입니다. 이러한 방식은 UI 응답성을 유지하는 데 유용합니다.
예를 들어...
문자 메시지를 보내면 그 메시지를 보낸 후에도 바로 다른 일을 할 수 있습니다. 메시지에 대한 답변은 나중에 도착하지만, 그동안 계속 다른 작업을 진행할 수 있습니다. 이렇게 기다리지 않고 동시에 여러 일을 진행하는 것이 비동기적 처리에 해당합니다.
코드 예제
개발자는 글보다는 코드로 이해하는게 쉽겠죠? 코드로 한번 알아보도록 하겠습니다.
Queue 생성
let serialQueue = DispatchQueue(label: "serialQueue")
let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
Default 값은 Serial 이기 때문에 Concurrent Queue를 만들기 위해서는 속성을 기입해 줘야 합니다.
Serial + Sync
Button("serial - sync") {
print("A")
serialQueue.sync {
print("B")
}
print("C")
}
// A
// B
// C
코드를 보고 한번 미리 결과를 한번 예상해보면 좋을 거 같아요. 어떠신가요? 예상한 결과 값이 맞으셨나요?
Button("serial - sync") {
print("A")
serialQueue.sync {
sleep(3)
print("B")
}
print("C")
}
// A
// B
// C
이번거는 어떠신가요?? 맞으셨나요? 맞으셨다면 아주 훌륭하십니다. 틀리신 분을 위해 살짝 설명을 해보겠습니다.
아마 ACB의 결과를 예측하신 분들은 sleep(3) 이 잠시 대기하는 메서드니깐 대기할 때 C가 먼저 출력하지 않을까?라고 생각하셨을 거 같은데요. 우리는 sync로 B를 출력하는 작업을 등록했기 때문에. 이 작업에 대한 결과가 올 때까지 기다려야 합니다.
Serial + Async
Button("serial - async") {
print("A")
serialQueue.async {
print("B")
}
print("C")
}
// A
// C
// B
한번 예측해 보시죠. 맞추셨나요? 우리는 작업을 비동기로 할당했기 때문에 응답이 오지 않더라고 기다리지 않고 다음 행동을 실행해야 합니다. 그 결과 ACB라는 결과가 나오게 됩니다.
Concurrent + Sync
Button("concurrent - sync") {
print("A")
concurrentQueue.sync {
sleep(3)
print("B")
}
print("C")
}
// A
// B
// C
이 부분에서 저는 조금 헷갈렸는데요. 지금은 간략하게 작성하고 이후에 따로 빼서 기록해 보도록 하겠습니다.
처음 생각은 Concurrent는 여러 여러 개의 Thread를 사용할 것이고 sync 작업이 다른 Thread에 배정되어 ACB가 출력될 것이라고 생각했습니다. 하지만, 결과는 ABC였는데요.
저의 생각은 일단 단일 Thread를 사용하고 있다는 생각이 듭니다. 그러면 concurrent의 여러 Thread로 작업을 분산 할당시켜 주는 내용이 틀리다는 결론이 되는데요. (이 부분은 나중에 직접 다뤄 보도록 하겠습니다.😎 바로가기 )
Concurrent + Async
Button("concurrent - async") {
print("A")
concurrentQueue.async {
print("B")
}
print("C")
}
// A or A
// C or B
// B or C
이와 같은 경우 cuncurrent 하게 작업을 할당하고 응답을 기다리지 않고 바로 실행할 수 있기 때문에 각 Thread에 할당된 작업의 실행 순서는 같지만, 서로 다른 Thread끼리의 순서는 엇갈릴 수 있습니다.
오늘도 화이팅입니다!
'iOS > Swift' 카테고리의 다른 글
| [Swift] Dispatch Queue 종류 (0) | 2025.02.21 |
|---|---|
| [Swift] Concurrent + Sync 에 대한 궁금증 (0) | 2025.02.19 |
| [Swift] GCD를 공부해봅시다. (0) | 2025.02.18 |
| [Swift]DispatchObject (0) | 2025.02.13 |
| [Swift] 프로토콜 (Protocol) (0) | 2025.02.06 |