내일배움캠프 42일차

임클·2025년 4월 29일

내일배움캠프

목록 보기
43/44
post-thumbnail

1. Swift 동시성 프로그래밍 개요

  1. 동시성 필요성
    • 네트워크 요청, 파일 I/O, 복잡한 계산처럼 메인 스레드를 막는 작업을 백그라운드에서 수행
    • UI 반응성을 유지하면서 여러 작업을 병렬 또는 병행 처리
  2. 동시성 vs 병렬성
    • 동시성(Concurrency): 여러 작업이 겉보기상 동시에 진행
    • 병렬성(Parallelism): 실제로 여러 CPU 코어에서 동시에 실행

2. Grand Central Dispatch (GCD)

2.1 주요 개념

  • DispatchQueue
    • serial 큐: 한 번에 하나의 작업만 실행
    • concurrent 큐: 가능한 만큼 여러 작업을 동시에 실행
  • 동기(sync) vs 비동기(async)
    • sync: 호출한 스레드가 작업 완료를 기다림
    • async: 즉시 반환하고 큐에 작업을 등록만 함
  • QoS(Quality of Service)
    • .userInteractive, .userInitiated, .utility, .background 등 우선순위 지정
  • DispatchGroup, DispatchSemaphore, DispatchBarrier
    • 그룹화, 동기화, 배리어(장벽) 처리 등에 사용

2.2 사용 예시

// 1) serial 큐에서 비동기 작업
let serialQueue = DispatchQueue(label: "com.example.serial")
serialQueue.async {
    print("Serial 작업 1")
}
serialQueue.async {
    print("Serial 작업 2")
}

// 2) concurrent 큐에서 동기 작업
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
concurrentQueue.sync {
    print("Concurrent sync 작업")
}

// 3) 글로벌 큐와 메인 큐
DispatchQueue.global(qos: .utility).async {
    // 백그라운드에서 무거운 작업
    let result = heavyCalculation()
    DispatchQueue.main.async {
        // UI 업데이트
        self.label.text = "\(result)"
    }
}

3. OperationQueue vs DispatchQueue

구분DispatchQueueOperationQueue
API 레벨C 기반(저수준), 간단·가벼움Objective-C 기반(NSOperation), 고수준
작업 단위closure 단위Operation 서브클래싱 또는 BlockOperation
의존성불가능addDependency(_:)로 Task 간 의존성 설정
취소불가(직접 구현 필요)cancel() 지원
최대 동시 작업 수직접 관리 불가maxConcurrentOperationCount 설정 가능

3.1 언제 무엇을 쓸까?

  • DispatchQueue
    • 간단한 비동기·동기 처리, 속도와 가벼움을 우선할 때
  • OperationQueue
    • Task 간 의존성, 취소, 상태 관리를 체계적으로 하고 싶을 때

4. 동시성 프로그래밍 문제와 해결 방법

4.1 Race Condition (경쟁 상태)

  • 문제: 여러 스레드가 같은 자원(변수, 배열 등)에 동시에 접근·수정
  • 증상: 예측 불가능한 값, 데이터 손상
  • 해결
    • serial queue로 접근 직렬화
    • DispatchSemaphore, NSLock 등 락 사용
    • Swift 5.5 이후 actor 도입으로 안전한 상태 관리
actor Counter {
    private var value = 0
    func increment() { value += 1 }
    func get() -> Int { value }
}

let counter = Counter()
Task {
    await counter.increment()
    print(await counter.get())
}

4.2 Deadlock (교착 상태)

  • 문제: 서로가 상대 작업 완료를 기다리며 무한 대기
  • 원인 예시:
    DispatchQueue.main.sync {    // 메인 스레드에서 sync 호출
        // 메인 큐에서 다시 main.sync → 교착
    }
    
  • 해결
    • 가능하면 sync 대신 async 사용
    • 큐 간 순환 호출 피하기
    • UI 업데이트는 항상 메인 큐에서 async로

4.3 Starvation (기아 현상)

  • 문제: 낮은 우선순위 작업이 실행 기회를 얻지 못함
  • 해결
    • QoS 설정 적절히 조정
    • 중요한 작업이 과도한 리소스를 차지하지 않도록 설계

0개의 댓글