ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Chapter 2: GCD & Operations
    Raywenderlich/Concurrency by Tutorials 2020. 7. 6. 01:59

    앱 동시성(concurrent)을 구현할 때 일반적으로 사용하는 API는 GCD라고하는 Grand Central DispatchOperations 두 가지가 있다. 이것은 서로 경쟁하는 기술이나 독점적으로 선택해야 할 것이 아니다. 실제로, Operations은 GCD 위에서 구축되어 있다.

     

    Grand Central Dispatch

    GCD는 Apple이 C의 libdispatch 라이브러리(library)를 구현한 것이다. 이것의 목적은 자원(resources)의 가용성(availability)에 따라 병렬(parallel)로 실행할 수 있는 작업(tasks)인 메서드(method) 또는 클로저(closure)를 대기열(queue)에 넣는 것이다. 그런 다음 사용 가능한 프로세서 코어(processor core)에서 작업을 실행한다.

    Apple의 문서(documentation)는 종종 클로저(closure) 대신 블록(block)이라 언급하는 경우가 있는데, 이는 Objective-C에서 사용된 이름이기 때문이다. 동시성(concurrency) 컨텍스트(context)에서 같은 것(interchangeable)으로 간주 할 수 있다.

    GCD는 구현시 스레드(threads)를 사용하지만, 개발자가 직접 스레드 관리를 걱정할 필요는 없다. GCD의 작업은 꽤 가벼운(lightweight) 편이다. Apple은 2009년 GCD 기술 개요(technical brief)에서 구현을 위해 15 개의 명령(instructions)만 필요한 반면, 기존 스레드(threads)를 만들려면 수백 개의 명령(instructions)이 필요할 수 있다고 언급했다.

    GCD가 관리하는 모든 작업은 GCD의 선입 선출(first-in first-out, FIFO) 대기열(queues)에 배치된다. 대기열(queue)의 각 작업은 시스템에서 완전히 관리하는 스레드 풀(pool of threads)에 대해(against) 실행된다.

    작업이 어느 스레드(thread)에 대해(against) 실행(execute)되는 지에 대해서는 보장(guarantee)하지 않는다.

     

    Synchronous and asynchronous tasks

    대기열(queue)에 배치된 작업은 동기(synchronously) 또는 비동기(asynchronously)로 실행될 수 있다. 작업을 동기(synchronously)로 실행하면 앱은 다음 작업으로 넘어가기 전 실행(execution)이 완료되기 전까지, 현재 실행 반복(current run loop)을 기다리고(wait) 차단(block)한다. 또는 비동기(asynchronously)로 실행되는 작업이 앱에서 즉시 실행된다. 이렇게하면 첫 번째 작업이 실행(executing)되는 동안 앱에서 다른 작업을 자유롭게 실행할 수 있다.

    대기열(queues)은 FIFO 기반이지만, 순서대로 작업이 완료되는 것을 보장하지는 않는다. FIFO 절차는 작업이 끝나는 시점이 아니라 시작할 때 적용되는 것이다.

    일반적으로 오래도록 지속적으로 실행되는 UI 이외의(long-running non-UI) 작업인 경우, 백그라운드(background)에서 비동기(asynchronously)로 수행한다. GCD는 다음과 같이 몇 줄의 코드로 클로저(closures)를 생성해 매우 간단하게 구현할 수 있다:

    // Class level variable
    let queue = DispatchQueue(label: "com.raywenderlich.worker")
    
    // Somewhere in your function
    queue.async {
        // Call slow non-UI methods here
        
        DispatchQueue.main.async {
            // Update the UI here
        }
    }

    DispatchQueue에 대한 모든 내용은 3장 "Queues & Threads"에서 배울 것이다. 일반적으로 대기열(queue)을 생성하여 백그라운드 스레드(background thread)에서 비동기(asynchronously)로 실행할 작업을 추가(submit)하고, 완료(complete)되면 코드를 다시 기본 스레드(main thread)에 위임(delegate)하여 UI를 업데이트한다.

     

    Serial and concurrent queues

    작업이 실행(submitted)된 대기열(queue)에도 직렬성(serial) 또는 동시성(concurrent)이 있다. 직렬 대기열(serial queues)은 연결된 단일 스레드(thread)만 있으므로, 주어진 시간에 단일 작업만 실행할 수 있다. 반면에 동시 대기열(concurrent queue)은 시스템에 필요한 만큼의 스레드(thread)를 사용할 수 있다. 스레드(thread)는 동시 대기열(concurrent queue)에서 필요에 따라 생성(created) 및 해제(released)된다.

    동시 대기열(concurrent queue)을 사용하겠다고 iOS에 알릴 수 있지만, 한 번에 둘 이상의 작업이 실행된다는 보장(guarantee)은 없다. iOS 기기가 완전히 작동하지 않고(bogged down) 앱 리소스가 부족한 경우, 단일 작업만 실행할 수도 있다.

     

    Asynchronous doesn’t mean concurrent

    처음에는 차이가 미묘해(subtle) 보이지만, 작업이 비동기(asynchronous)라고 해서 항상 동시(concurrently)에 실행되는 것은 아니다. 실제로 직렬 대기열(serial queue) 또는 동시 대기열(concurrent queue)로 비동기(asynchronous) 작업을 실행(submit)할 수 있다. 동기식(synchronous) 또는 비동기식(asynchronous)은 작업을 실행중인 대기열(queue)이 다음 작업을 생성하기 전에 이전 작업이 완료(complete)되기를 기다려야하는지 여부를 확인하는 것에 불과하다.

    반면에 직렬(serial)과 동시(concurrent)로 분류하면, 대기열(queue)에 사용 가능한 스레드(threads)가 단일(single)인지 다중(multiple)인지 여부로 식별한다. 직렬 대기열(serial queue)에 세 개의 비동기(asynchronous) 작업을 제출(submitting)하는 것을 생각해 보면, 사용 가능한 스레드(thread)가 하나만 있으므로 다음 작업을 시작하기 전에 각 작업을 완전히 완료해야 한다.

    즉, 작업이 동기적(synchronous)이거나 그렇지 않은 작업은 작업의 소스(source)에 관한 것이다. 연속적(serial)이거나 동시적(concurrent)이라는 것은 작업의 목적(destination)에 관한 것이다.

     

    Operations

    GCD는 백그라운드(background)에서 한 번만 실행해야 하는 일반적인 작업에 적합하다. 이미지 편집 작업과 같이 재사용(reusable)할 수 있는 기능(functionality)을 구축해 클래스(class)로 캡슐화(encapsulate)할 수 있다. Operation을 서브 클래싱(subclassing)함으로써 그 목표를 달성 할 수 있다

     

    Operation subclassing

    Operations은 GCD용 DispatchQueue에 해야할 작업의 클로저(closure)를 제출(submit)하는 것처럼, OperationQueue에 제출(submitted)할 수 있는 모든 기능을 갖춘 클래스(fully-functional classes)이다. 이는 클래스(classes)이고 변수(variables)를 포함 (contain)할 수 있기 때문에, 주어진 시점에서의 operation 상태(state)를 알 수 있다.

    Operations의 상태(states)는 다음과 같다:

    • isReady
    • isExecuting
    • isCancelled
    • isFinished

    GCD와 달리 operation은 기본적으로 동기적(synchronously)으로 실행(run)되며, 비동기(asynchronously)로 실행(run)하려면 더 많은 작업이 필요하다. 직접 operation을 수행할 수 있지만, 동기(synchronous)적인 특성을 가지고 있기 때문에 결코 좋은 생각은 아니다. UI 성능에 영향을 미치지 않도록 OperationQueue에 제출(submitting)하여 기본 스레드(main thread)를 가져와야 한다.

     

    Bonus features

    하지만, Operations을 사용하면 작업 취소(cancelling the task), 작업 상태 보고(reporting the state of the task), 비동기 작업 래핑(wrapping asynchronous tasks), 다양한 작업 간의 종속성 지정(pecifying dependences between various tasks) 등과 같은 일반적인 요구사항을 처리 할 수 ​​있으므로 보다 효과적으로 작업을 제어할 수 있다. 6장, "Operations"에서 앱에서 operations을 사용하는 방법에 대해보다 심도있게 설명한다.

     

    BlockOperation

    때로는 앱에서 operations을 많이 사용하는 앱에서도, 더 단순한 GCD와 같은 클로저(closure)가 필요한 경우가 있다. DispatchQueue를 생성하지 않으려면, BlockOperation 클래스(class)를 대신 사용할 수 있다.

    BlockOperation는 사용자를 위해 Operation을 서브 클래스(subclasses)하고 기본 글로벌 대기열(default global queue)에서 하나 이상의 클로저(closures)의 동시(concurrent) 실행(execution)을 관리한다. Operation의 서브 클래스(subclass)가 되면, operation의 다른 모든 기능을 활용할 수 있다.

    Block operations은 동시(concurrently)에 실행된다. 직렬(serially)로 실행해야하는 경우, dispatch queue를 대신 사용해야 한다.

     

    Which should you use?

    앱에서 GCD와 Operations 중 어느 것을 사용해야 하는지에 대한 명확한 지침은 없다. GCD는 단순히 실행하고 잊어버려도 되는 간단한 작업에 더 적합하다. Operations은 작업의 추적(track), 취소(cancel) 등의 훨씬 더 많은 기능(ability)을 제공한다.

    실행(executed)해야하는 메서드(methods) 또는 코드 덩어리(chunks of code)로 작업하는 경우에는 GCD가 적합하다. 데이터와 기능을 캡슐화(encapsulate)해야하는 객체(objects)로 작업하는 경우에는 Operations을 더 많이 활용할 수 있다. 일부 개발자는 Apple의 지침에 따르면 항상 최고 수준(highest level)의 추상화(abstraction)를 사용하는 것을 권장하고, Operations이 GCD를 기반으로 구축되어 있기 때문에 항상을 operations을 사용해야 한다고 말하기도 한다.

    결국 가장 적합한 기술을 사용해야하며, 프로젝트(project) 또는 특정 사용 사례(specific use-case)의 장기 지속성(long-term sustainability)을 고려해야 한다.

    다음 장(chapter)에서는 Grand Central Dispatch의 작동 방식에 대해 자세히 알아보고, 스레드(threads)와 대기열(queues)의 차이점, 앱에서 동시성(concurrency)을 구현할 때 발생할 수 있는 몇가지 문제점(complexities)을 살펴본다.

    'Raywenderlich > Concurrency by Tutorials' 카테고리의 다른 글

    Chapter 6: Operations  (0) 2020.07.09
    Chapter 5: Concurrency Problems  (0) 2020.07.09
    Chapter 4: Groups & Semaphores  (0) 2020.07.08
    Chapter 3: Queues & Threads  (0) 2020.07.06
    Chapter 1: Introduction  (0) 2020.07.05
Designed by Tistory.