GCD (Grand Central Dispatch)
- Apple이 개발한 멀티스레드 프로그래밍을 쉽게 구현할 수 있도록 제공하는 프레임워크
- 효율적인 동시성(concurrency) 처리를 지원하며 주로 비동기 작업 관리에 사용됨
GCD의 특징
- 저수준 스레드 관리
- 스레드 생성 및 관리를 시스템이 자동으로 처리함
- 개발자는 직접 스레드를 생성하거나 종료할 필요 없이 작업 큐에 태스크를 제출하면 됨
- 작업 큐 (Task Queue) 기반
- 작업은 큐에 추가되며, 시스템은 적절한 스레드 풀을 사용하여 태스크를 실행함
- FIFO(First In, First Out) 순서를 따르며, 우선순위에 따라 실행 순서를 조정함
- 성능 최적화
- GCD는 CPU 코어 수에 따라 태스크를 효율적으로 분배함
- 필요한 만큼의 스레드만 생성하여 리소스 낭비를 방지
- 비동기/동기 실행
- 작업은 비동기적, 동기적으로 실행 가능
GCD의 핵심 개념
1. 디스패치 큐 (Dispatch Queue)
- 작업을 순차적 또는 동시에 실행할 수 있는 큐
- Serial Queue (직렬 큐)
- 한 번에 하나의 작업만 처리
- FIFO 순서로 실행됨
- Concurrent Queue (동시 큐)
- 여러 작업을 동시에 처리함
- 작업 시작은 FIFO 순서에 따라 이루어지지만, 완료 순서는 보장되지 않음
- Main Queue
- 메인 스레드는 단 하나만 존재
- Serial 특성을 가짐
- UI 업데이트 담당
- Global Queue
- 시스템이 제공하는 기본 Concurrent Queue
- QoS(Quality of Service)에 따라 작업 우선순위를 지정 가능함
- Custom Queue
- 개발자가 직접 생성하는 큐
- Serial 또는 Concurrent로 설정 가능
2. 동기와 비동기(Synchronous vs Asynchronous)
특성 동기(Sync) 비동기(Async)
특성 | 동기(Sync) | 비동기(Async) |
실행 방식 | 호출한 작업이 완료될 때까지 기다림 | 작업을 바로 큐에 전달하고, 호출자는 즉시 반환 |
스레드 블로킹 | 호출 스레드(blocking) 발생 | 호출 스레드(blocking) 없음 |
용도 | 순차적인 작업 처리에 사용 | 작업 병렬 처리, 비동기 작업 처리에 사용 |
GCD의 주요 함수
1. Global Queue
- QoS(Quality of Service) 설정
DispatchQueue.global(qos: .background).async {
print("Running in the background queue.")
}
2. Main Queue
- UI 업데이트는 반드시 main 큐에서 실행해야 함
DispatchQueue.main.async {
print("Updating UI on the main queue.")
}
3. Dispatch Group
- 여러 작업을 그룹화하여 모든 작업이 완료된 시점에 특정 코드를 실행할 수 있도록 함
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
print("Task 1")
}
DispatchQueue.global().async(group: group) {
print("Task 2")
}
group.notify(queue: .main) { // 같은 그룹 내의 작업이 모두 완료되면 호출됨
print("All tasks are completed.")
}
4. Serial Queue
- FIFO 순서로 순서가 언제나 보장됨 → 들어온 순서대로 작업
var numArr = [1, 2, 3, 4, 5]
let serialQueue = DispatchQueue(label: "serial") // default 값은 serial
numArr.map { num in
serialQueue.async {
print(num) // 순서가 언제나 같음
}
}
/*
1
2
3
4
5
*/
5. Concurrent Queue 사용
- 여러 작업을 동시에 처리하기 때문에 완료 순서가 보장되지 않음
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
numArr.map { num in
concurrentQueue.async {
print(num) // 순서가 보장되지 않고 작업이 끝나는대로 출력
}
}
/*
1
2
4
3
5
*/

GCD 주의사항!
1. 메인 스레드에서의 main.sync → 데드락 발생
- 메인 큐는 직렬 큐로 동작함
- 동기 작업(sync)은 호출한 스레드를 블로킹하므로 메인 스레드에서 DispatchQueue.main.sync를 호출하면 데드락이 발생함
var numArr = [1, 2, 3, 4, 5]
numArr.map { num in
DispatchQueue.main.sync { // error: Execution was interrupted
print(num)
}
}
- Main DispatchQueue는 Serial 특성을 가지는데 sync를 호출하면 메인 스레드는 그대로 진행을 멈추고 등록한 작업이 끝날 때까지 기다림
- Serial 스레드는 호출 스레드를 블로킹함 → 메인 스레드를 블로킹
- 동시에 큐에 등록된 작업(print(num))은 메인 스레드에 할당됨
- 메인 스레드는 등록된 작업을 시작하지 못하고 작업이 끝나지 않는 데드락 상태에 빠짐
해결 방법
→ 메인 큐에서 동기 작업 호출을 피하고 비동기 작업(async)로 처리
DispatchQueue.main.async { ... }
2. 직렬 큐에서 sync → 데드락 발생
let serialQueue = DispatchQueue(label: "serial") // 직렬 큐
serialQueue.async {
serialQueue.sync {
print("DeadLock")
}
}
- serialQueue.async로 작업이 등록됨
- 작업이 실행 중일 때, 동일한 직렬 큐에서 sync를 호출하여 새로운 작업을 등록함
- 직렬 큐는 한 번에 하나의 작업만 실행하므로, 현재 실행 중인 작업이 완료 되어야 다음 작업이 실행됨
- 현재 작업은 sync 호출로 인해 대기 상태가 되므로 데드락이 발생함
→ 가능한 한 글로벌 큐에서 작업이 서로 의존하지 않도록 설계해야 함
'Swift' 카테고리의 다른 글
Swift - 커스텀 폰트 적용 방법 및 활용 방법 (0) | 2025.01.05 |
---|---|
Swift - 클로저의 캡처 파해치기 (값 타입, 참조 타입의 동작 차이) (0) | 2024.12.11 |
Swift - Dispatch를 이용한 성능 개선 (1) | 2024.12.09 |
Swift - ARC (Automatic Reference Count) 정리 및 강한 순환 참조 해결 (2) | 2024.12.08 |
Swift - Struct와 Class 선택과 차이점 (0) | 2024.12.06 |