Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

J

[Swift] GCD 본문

Swift

[Swift] GCD

yujaehui 2024. 4. 13. 00:07

GCD (Grand Central Dispatch)

GCD는 Apple이 제공하는 비동기 작업과 동시성 처리를 위한 저수준 API.

멀티코어 프로세서의 성능을 최대로 활용할 수 있도록 도와주며, 스레드 관리를 자동 처리하여 개발자가 직접 스레드를 생성하고 관리하는 부담을 덜어 줌.

Swift에서는 GCD를 사용하여 백그라운드 작업, UI 업데이트, 파일 입출력, 네트워크 요청과 같은 작업을 효율적으로 처리할 수 있음.


1. GCD의 주요 개념

1.1. 작업 단위: 작업(Tasks)

GCD에서는 모든 작업을 작업 단위로 처리.

이 작업은 비동기로 실행할 수 있으며, 각각의 작업은 클로저 형태로 작성.

GCD는 개발자가 요청한 작업을 큐에 적재하고, 적절한 스레드에서 병렬로 처리.

1.2. 큐: Dispatch Queue

Dispatch Queue는 작업을 순차적 또는 병렬적으로 처리되는 대기열.

GCD에서는 작업을 큐에 적재하고, 큐에 적재된 작업은 GCD가 적절한 시점에 실행.

  • 직렬 큐(Serial Queue)
    • 작업을 하나씩 순서대로 처리.
    • 작업의 순서 보장이 필요할 때 사용.
  • 동시 큐(Concurrent Queue)
    • 여러 작업을 동시에 병렬로 실행 가능.
    • 작업의 완료 순서가 일정하지 않음.

2. Dispatch Queue

2.1. 메인 큐 (Main Queue)

메인 큐는 UI 업데이트와 같이 메인 스레드에서 실행되어야 하는 작업을 처리하는 큐.

메인 큐는 직렬 큐로, 메인 스레드에서 실행되므로 UI와 관련된 작업은 반드시 이 큐에서 실행해야 함.

DispatchQueue.main.async {
    // 메인 스레드에서 실행되는 코드 (UI 업데이트 등)
}

2.2. 글로벌 큐 (Global Queue)

글로벌 큐는 GCD에서 제공하는 공유된 동시 큐. 작업의 우선순위를 지정해 여러 작업을 동시에 처리할 수 있음.

GCD는 작업의 우선순위를 설정하여 효율적인 스케줄링을 지원.

DispatchQueue.global(qos: .userInitiated).async {
    // 비동기적으로 실행되는 코드
}
  • QOS (Quality of Service)
    • 작업의 중요도를 나타내는 값.
    • `userInteractive`, `userInitiated`, `default`, `utility`, `background` 등의 순서로 우선순위 설정 가능.

2.3. 커스텀 직렬 큐 (Custom Serial Queue)

커스텀 직렬 큐는 개발자가 직접 정의한 큐로, 작업을 하나씩 순서대로 처리할 수 있음.

주로 작업의 순서를 보장해야 할 때 사용.

let customQueue = DispatchQueue(label: "com.example.mySerialQueue")

customQueue.async {
    // 직렬 큐에서 실행될 작업
}

2.4. 동기 vs 비동기

  • 동기(Synchronous)
    • 작업이 완료될 때까지 현재 스레드 블로킹.
    • 즉, 작업이 끝날 때까지 다음 작업을 수행하지 않음.
  • 비동기(Asynchronous)
    • 작업이 백그라운드에서 비동기적으로 실행되며, 현재의 작업이 끝나지 않더라도 다른 작업을 계속 실행할 수 있음.
// 동기
DispatchQueue.global().sync {
    print("동기 작업")
}

// 비동기
DispatchQueue.global().async {
    print("비동기 작업")
}

3. 메인 큐와 글로벌 큐에서 동기/비동기 작업 처리

3.1. 메인 큐에서 동기 작업 처리

DispatchQueue.main.sync {
    print("메인 큐에서 동기 작업")
}
print("다음 작업")
  • 메인 큐에서 동기 작업을 실행하면 교착 상태(deadlock)가 발생할 수 있음.
  • 메인 스레드에서 실행 중인 코드가 이미 메인 큐를 점유하고 있기 때문에, 메인 큐에서 다른 동기 작업을 실행하려 하면 작업이 완료되지 않고 멈추는 현상 발생.

3.2. 메인 큐에서 비동기 작업 처리

DispatchQueue.main.async {
    print("메인 큐에서 비동기 작업")
}
print("다음 작업")
  • 메인 큐에서 비동기 작업은 현재 스레드를 블로킹하지 않음.
  • 먼저 "다음 작업"이 출력된 후, "메인 큐에서 비동기 작업"이 실행.
  • 비동기 작업으로 메인 스레드의 작업이 완료될 때까지 기다리지 않고 바로 다음 코드를 실행.

3.3. 글로벌 큐에서 동기 작업 처리

DispatchQueue.global().sync {
    print("글로벌 큐에서 동기 작업")
}
print("다음 작업")
  • 글로벌 큐에서 동기 작업을 실행하면 "글로벌 큐에서 동기 작업"이 출력된 후 "다음 작업"이 출력.
  • 동기 작업이므로 작업이 완료될 때까지 기다렸다가 다음 코드를 실행.

3.4. 글로벌 큐에서 비동기 작업 처리

DispatchQueue.global().async {
    print("글로벌 큐에서 비동기 작업")
}
print("다음 작업")
  • 글로벌 큐에서 비동기 작업을 실행하면, "다음 작업"이 출력된 후 "글로벌 큐에서 비동기 작업"이 출력.
  • 비동기 작업이므로 글로벌 큐에서 작업이 끝나기 전에 다음 작업이 바로 실행.

4. DispatchGroup

`DispatchGroup`은 여러 개의 비동기 작업을 그룹화하여 관리하고, 그룹 내의 모든 작업이 완료될 때까지 기다릴 수 있도록 해줌.

여러 개의 비동기 작업을 동시에 실행하고, 모든 작업이 완료되었을 때 특정 코드를 실행해야 할 때 유용.

4.1. DispatchGroup 사용 예시

let group = DispatchGroup()

let queue = DispatchQueue.global()

queue.async(group: group) {
    print("작업 1 시작")
    sleep(2) // 작업 1 처리
    print("작업 1 완료")
}

queue.async(group: group) {
    print("작업 2 시작")
    sleep(1) // 작업 2 처리
    print("작업 2 완료")
}

// 모든 작업이 완료되면 실행
group.notify(queue: DispatchQueue.main) {
    print("모든 작업 완료")
}
  • `group.notify`는 그룹 내 모든 작업이 완료되었을 때 실행.
  • `sleep`은 작업 처리 중인 것처럼 시뮬레이션하기 위해 사용.

4.2. `enter`와 `leave` 사용

`enter`와 `leave`는 `DispatchGroup`에서 비동기 작업을 수동으로 시작하고 완료하는 데 사용.

`enter`는 그룹에 작업을 추가하고, `leave`는 해당 작업이 완료되었음을 알림.

let group = DispatchGroup()
let queue = DispatchQueue.global()

group.enter()
queue.async {
    print("작업 1 시작")
    sleep(2)
    print("작업 1 완료")
    group.leave()
}

group.enter()
queue.async {
    print("작업 2 시작")
    sleep(1)
    print("작업 2 완료")
    group.leave()
}

group.notify(queue: DispatchQueue.main) {
    print("모든 작업 완료")
}
  • `group.enter()`로 작업을 추가하고, 작업이 끝나면 `group.leave()`를 호출하여 완료를 알림
  • 두 작업이 완료되면 "모든 작업 완료"가 출력.

4.3. leave와 enter 주의 사항

`enter()` 호출 횟수와 `leave()` 호출 횟수가 다르면 디스패치 그룹이 종료되지 않음.

즉, 그룹에 `leave()`를 호출하지 않으면 그룹은 작업이 완료되지 않은 것으로 간주.


5. GCD와 Swift Concurrency 비교

  GCD (Grand Central Dispatch) Swift Concurrency
사용 방식 클로저와 큐를 사용한 비동기 작업 관리 `async`/`await`를 사용한 비동기 작업 관리
동시성 제어 `DispatchQueue`, `DispatchGroup`, `Semaphore` 등 `Task`, `TaskGroup`, `actor` 등을 사용하여 동시성 제어
코드 가독성 클로저 중첩으로 인해 복잡해질 수 있음 동기 코드처럼 비동기 코드를 작성 가능
에러 처리 수동으로 에러 처리 비동기 함수에서 `throws`로 에러 처리 가능
성능 멀티코어 CPU 활용 최적화 `Task` 및 `actor`로 경량화된 비동기 처리

6. GCD 사용 시 주의 사항

  • 동시성 문제
    • GCD로 비동기 작업을 처리할 때, 여러 스레드에서 동일한 리소스에 접근할 경우 경쟁 상태(race condition)가 발생할 수 있기에 동기화로 문제를 해결해야 함.
  • 메인 스레드에서의 작업
    • UI 업데이트는 반드시 메인 큐에서 처리해야 함.
    • 비동기적으로 백그라운드에서 작업을 수행한 후, 메인 스레드에서 UI를 업데이트.

6. 정리

GCD는 Swift와 iOS에서 비동기 작업과 동시성 처리를 위해 제공되는 강력한 API.

`DispatchQueue`, `DispatchGroup`, `DispatchSemaphore`와 같은 다양한 기능으로 복잡한 동시성 작업을 효율적 관리 가능.

그러나 GCD의 클로저 기반 코드는 가독성이 떨어질 수 있으며, 이러한 단점을 해결하기 위해 Swift Concurrency가 도입.

  • GCD는 고성능 동시성 처리를 지원하지만, 적절한 동기화와 리소스 관리가 필요.
  • Swift Concurrency는 코드 가독성을 개선하고 에러 처리를 더 쉽게 할 수 있도록 설계 됨.

'Swift' 카테고리의 다른 글

[Swift] GCD vs Swift Concurrency: 스레드 관리와 성능  (0) 2024.04.15
[Swift] Swift Concurrency  (0) 2024.04.15
[Swift] ARC  (0) 2024.04.13
[Swift] mutating  (0) 2024.04.12
[Swift] inout  (0) 2024.04.08