 Apple Lover Developer & Artist

영속적인 디자인에 현대의 공감을 채워넣는 공방입니다

 Apple/iOS Dev Challenges

[Challenge] GCD

singularis7 2021. 12. 17. 00:23
반응형

Overview

스레드는 실행중인 코드의 흐름을 관리해준다. 과거의 컴퓨팅 환경에서는 모든 코드가 단일 스레드 환경에서 실행되었지만 iOS와 같은 현대적인 운영체제에서는 여러가지 스레드가 여러 코드의 흐름을 동시에 실행시킬 수 있도록 지원해준다. (더군다나 오늘날의 멀티 코어 프로세서를 사용하면 스레드는 서로 다른 코어에서 실행될 수 있으나, 현재 사용하고 있는 컴퓨터가 싱글 코어일지라도 CPU 가상화를 통해 마치 동시에 실행되는 것처럼 보이는 환상을 준다) iOS 에서는 모든 사용자 인터페이스와 연관된 코드를 실행시킬 때에 main 스레드를 활용한다.

Grand Central Dispatch (GCD)

Grand Central Dispatch (GCD)는 iOS에서 제공하는 기술이며 시스템 큐에서 dispatch 함으로써 개발자가 비동기식으로 동작하는 코드를 작성할 수 있도록 도와준다. 여기서 시스템 큐라는 친구는 GCD에 의해 관리되는데 하나 혹은 그 이상의 스레드를 실행시켜주는 역할을 한다.

GCD가 스레드 관리와 같은 복잡한 부분을 대신 처리해주기 때문에 개발자가 별도로 관리해줄 필요가 없다는 이점이 있다! GCD 는 하나의 main 큐를 제공하며 이 큐는 main 스레드에서 동작하여 언제나 높은 가중치를 갖게 된다. 그리고 다른 background queue는 가중치가 변화하여 실행한다. background queue는 연산의 처리 시간이 오래걸리는 작업을 실행시키는 용도로 활용할 수 있다. 이런 작업이 main 스레드에서 실행된다면 사용자 입장에서는 앱이 멈춰있는 것처럼 보일 것이다.

이전 URLSession 메소드는 결과를 처리하려면 요청이 완료될 때 백그라운드 큐에서 실행되는 클로져가 필요하다. 이전 방법을 사용하여 클로져에서 사용자 인터페이스 요소를 업데이트해야 하는 경우 사용자 인터페이스 코드를 main queue로 전송해야한다. 이때 가장 높은 우선 순위 수준으로 처리된다! 이렇게 구현하려면 DispatchQueue를 사용하여 기본 큐에서 코드 블록을 실행해야 하며 다음과 같은 코드를 작성해볼 수 있다.

DispatchQueue.main.async {
    // Code here will be executed on the main queue
}

DispatchQueue 는 여러가지 시스템 큐와 함께 동작하게 되는데 위 코드에서 main 키워드는 main queue 를 지칭하며 async 키워드는 main queue 에게 뒤따르는 코드 블럭을 가능한 빠르게 처리해달라고 말해주는 것이다.

왜냐하면 이미 코드에서 async 메서드와 task를 사용했기 때문에, 위와 같은 기계적 절차를 다룰 필요는 없으나, Swift Concurrency 를 다루지 않는 코드를 사용하는 경우 GCD 를 통해 병행성을 구현해야 한다.


WWDC 2015 GCD Session

GCD 의 탄생 배경

Mac OS X Snow Leopard 버젼에서 최초로 공개된 기술이다. 당시에 출시된 Macbook Pro는 Core 2 Duo CPU를 사용하고 있었으며 GCD 의 selling point는 어플리케이션이 병행성으로 실행될 때 두개의 코어를 잘 활용한다는 부분이었으며 thread 를 처리하는 것을 매우 쉽게 만들어주는 것이었다. 오늘날의 맥은 무수히 많은 코어를 갖고 있기도 하기에 GCD가 좀 더 힘을 발휘할 수 있는 환경으로도 볼 수 있다.


목차

  • Quality of Service Introduction
  • GCD Design Patterns with QoS
  • Threads, Queues, and Run Loops
  • GCD and Crash Reports

만약 실행 흐름이 하나일 때 main thread 에서 네트워크나 데이터베이스에 접근하는 등의 무거운 작업을 수행하는 경우 UI가 responsive 하게 반응하지 못한다는 단점이 존재한다!

위와 같은 상황이 GCD가 힘을 발휘할 수 있는 영역이다.  GCD Queue 를 만든 후에 dispatch async 를 활용하여 main 스레드에서 처리하던 작업을 GCD 큐로 옮겨올 수 있다. 따라서 개발자가 작성한 코드에 main thread의 측면에서 보았을 때 비동기적으로 실행된다.

위 구조로 작성하면 데이터를 처리하는 동안에도 main queue 가 별개의 일을 처리할 수 있기 때문에 UI가 여전히 responsive 하게 동작한다는 점이다.    


근데 한번쯤 생각해볼 만한 부분이 있다! CPU가 하나인 상황에서 처리해야 하는 실행 흐름이 main thread 와 GCD 큐 두개가 있는 경우 어떤 스레드가 실행되어야할까? 이 문제를 해결하기위해 Quality of Service 클래스가 도입되었다. QoS 에는 다음과 같은 4가지 종류의 클래스가 정의되어있다.

위 클래스를 활용하여 개발자는 시스템에게 어떤 종유의 작업을 처리할지 설명해줄 수 있으며 시스템은 이 정보를 기반으로 코드를 효율적으로 실행시도록 시스템 자원을 제어하고 관리해준다. 그렇다면 시스템 자원 제어에 관한 지원은 어떤 종류가 있을까?

  • CPU scheduling priority : 어떤 스레드를 무슨 순서로 실행시킬지에 관한 내용이다.
  • I/O priority : 시스템에서 다른 I/O가 실행되었을 때 어떻게 I/O를 실행시킬 것인가?
  • Timer coalescing : 전원 절약 기능이다. 
  • CPU throughput vs. efficiency

가장 이상적인 상황에서는 개발자가 코드와 실행 환경에 알맞게 위와 같은 요소를 구성해주는 방법이 있을테지만 아주 복잡하기 때문에 자원을 관리해주도록 위와 같은 4가지 상황으로 추상화해둔 것 같다! 개발자가 작성한 코드가 수행하는 작업의 의도를 4가지로 분류하여 시스템에게 코드의 의도를 설명해주면 QoS가 적합한 자원 제어에 관한 구성값을 설정해준다. 각 QoS 를 자세하게 뜯어보자!

User Interactive : main thread에서 동작한다. 예를 들어 iOS 앱에서 드래그하는 상황을 생각해보면 main thread는 유저의 입력에 따라 다음 애니메이션을 60번 그리기 위해 응답해야 한다. 따라서 좀더 적극적으로 UI를 갱신하는 코드의 경우 User Interactive 에 넣어두는 것도 좋은 방법이 될 수 있다.

User Initiated 사용자가 어플리케이션에서 의미있는 progress를 수행하지 않으면 User Initiated 가 적합한 클래스가 될 수 있다!

Utility  사용자가 시작했던 자동으로 시작되었으나 처리 시간이 오래 소요되는 작업이며 사용자가 앱을 사용하는 것을 막지 않을 때 적합하다. 무언가를 다운로드 받는 것처럼 사용자가 무언가를 다운로드 받는 상황을 떠올려볼 수 있다.

Background 사용자가 적극적으로 보지 않는 나머지 모든 작업이다. 작업을 정리한다던지 등등


Reference

  • Swift App Development (Apple Books) - Data Collection
  • 내일은 10분 이후로 마저 정리해봐야지!
반응형