본문 바로가기

iOS 개발/App 개발 관련

[iOS] GCD, 비동기 프로그래밍

참고 자료 

Apple 공식 문서 Dispatch

Apple 공식 문서 DispatchQueue

Stackoverflow GCD 관련

Apple 공식 문서 Concurrency Programming Guide

 

 

목차 (눌러서 이동)

 

    비동기 프로그래밍, Grand Central Dispatch(GCD)

    Apple 의 UI 는 Main thread 에서만 업데이트 되도록 디자인되어 있다. UIKit 가 thread-safe 하지 않아 Main thread 에서 순차적으로 처리하는 것이 안정적이기 때문이다. (UIKit 가 thread-safe 하지 않은 이유.)

     

    Main thread 가 UI 를 업데이트 할 동안 네트워킹과 같은 무거운 작업들을 다른 thread 에서 동시에 작업할 수 있다면 앱 성능 향상에 효과적일 것이다. 이것이 비동기 프로그래밍이 필요한 이유이다.

     

    Apple system 에서는 비동기 프로그래밍을 위해 GCD 를 사용한다. GCD 는 멀티코어 하드웨어에서 다수의 code block 을 동시 실행하도록 지원하는 Framework 이다.

     

    비동기 프로그래밍을 위해 GCD 를 사용한다는 것은 비동기적으로 처리하고 싶은 code block(task) 을 DispatchQueue 에 push 한다는 의미이다. 비동기 작업을 DispatchQueue 에게 맡기기만 하면 알아서 효율적으로 처리하기 때문에 개발자가 threading 에 관여할 필요가 없어진다.

    DispatchQueue

    비동기적 수행을 위해 push 된 task 들을 관리하고 처리하는 객체이다. 시스템의 가용한 자원에 따라 thread 의 생성과 사용을 관리하여 적절한 thread 가 task 들을 처리하도록 한다. Queue 의 종류로는 Serial queue, Concurrent queue, Main dispatch queue 가 있다.

    DispatchQueue types

    Serial queue 는 Private dispatch queue 라고도 불리며 한 번에 하나씩 push 된 순서대로 작업을 수행한다. 때문에 특정 자원에 대한 접근을 통제할 때 자주 사용된다. 필요한 만큼 생성할 수 있으며 헷갈리지 말아야 할 것은 하나의 Serial queue 내의 작업들을 순차적으로 수행한다는 것이지 서로 다른 Serial queue 들의 작업들은 동시에 수행될 수도 있다는 점이다.

     

    let serialQueue = DispatchQueue(label: "serial")

     

    Concurrent queue 는 Global dispatch queue 라고도 불리며 하나 이상의 작업을 동시에 수행할 수 있다. (push 된 순서대로 수행되는 것은 동일.) Private dispatch queue 에도 option 을 주어 concurrent 하게 수행하는 queue 를 생성할 수 있지만 특별한 이유가 없다면 시스템에서 미리 정의하는 Global dispatch queue 를 사용하는 것이 권장된다.

     

    // It's possible to create a private concurrent dispatch queue.
    let concurrentQueue = DispatchQueue(label: "concurrent", attributes: [.concurrent])
    // But predefined global dispatch queue is recommended.
    let concurrentQueue = DispatchQueue.global(qos: .default)

     

    Main dispatch queue 는 전역적으로 사용 가능한 serial queue (작업을 순차적으로 처리.) 이고, 앱의 Main thread 에 작업을 수행시킨다.

    Queueing options (sync or async)

    DispatchQueue 에 작업을 push 할 때 두 가지 방식으로 scheduling 할 수 있다. 

     

    Synchronous 하게 push 하면 code block 이 완료될 때까지 기다린다.

     

    DispatchQueue.global(qos: .default).sync { /* task */ }

     

    Asynchronous 하게 push 하면 하던 작업을 계속 진행하면서 다른 어딘가 에서 task 가 동시에 수행된다.

     

    DispatchQueue.global(qos: .default).async { /* task */ }

    Code Example

    온라인 이미지를 다운로드 받아 UI 에 적용한다고 하자. 이미지 다운로드와 같은 시간이 걸리는 작업은 Global queue 를 사용하고 UI 업데이트는 Main queue 를 사용하므로 다음과 같이 처리할 수 있다.

     

    // You want your app to prepare the other things while
    // setting up the image. So, push this task to the 
    // global queue asynchronously.
    DispatchQueue.global(qos: .userInteractive).async {
        // Download image from the online.
        let image = downloadImage()
        
        // UI update should be execute on the main queue.
        DispatchQueue.main.async {
            updateUI(image: image)
        }
    }