[iOS] GCD, Dispatch Queue

ungchun·2022년 5월 3일
0
post-thumbnail

GCD, Dispatch Queue iOS 앱 개발을 하면 필수로 알아야하는 개념인데 매번 까먹어서 정리해보려한다.

우리는 보통 GCD, Dispatch Queue를 구분하지 않고 부른다. 요번에 정리하며 알게됐는데 공식문서를 보면 GCD는 동시성 실행을 제공하는 프로그래밍 언어 요소, 런타임 라이브러리 등이라고 합니다. 그래서 따지고 보면 GCD랑 Dispatch Queue 는 같지는 않지만 GCD의 개념으로 동시성 프로그래밍을 지원하는 친구가 Dispatch Queue 라서 같은 의미로 봐도 무방할 것 같습니다.

Dispatch Queue

다른 언어와 달리 iOS 는 Dispatch Queue에 작업을 넣으면 운영체제가 알아서 스레드에 할당해줍니다.

Serial, Concurrent

Dispatch Queue에는 2가지 종류가 있는데 Serial과 Concurrent가 있습니다.

  • Serial : 등록된 작업들을 한 번에 하나씩 처리
  • Concurrent : 등록된 작업들을 여러작업으로 (여러 스레드) 처리

기본은 Serial 이며 Concurrent 로 작업하려면 attributes를 따로 세팅해야합니다.

let dispatchQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)

추가로 Private Queue (커스텀 큐) 으로도 만들 수 있는데 다음과 같이 사용하면 된다.

let queue = DispatchQueue(label: "mySerialQueue")
let queue2 = DispatchQueue(label: "myConcurrentQueue", attributes: .concurrent)

Sync, Async

그리고 Sync, Async 를 지정할 수 있는데 Sync는 동기, 큐에 추가된 작업이 완료될 때 까지 기다리는 것이고, Async는 비동기, 큐에 작업을 등록하면 완료 여부와는 상관없이 계속 작업을 진행하는 것입니다.

Main, Global

Dispatch Queue를 사용할 때 기본적으로 2가지의 queue를 제공하는데 바로 main과 global 입니다.

  • Main Queue : Main Queue는 메인 쓰레드에서 작업을 실행하며 Serial의 특성을 가집니다. 그리고 UI의 업데이트에 관련된 모든 작업은 Main Queue 에서 작동되어야 합니다. 그리고 sync는 작업이 끝날 때까지 기다리기 때문에 UI 업데이트를 담당하는 main queue는 main.sync 를 하면 안된다고 합니다. (자세한 내용은 Zedd님이 잘 정리해놓으셔서 iOS ) 왜 main.sync를 하면 안될까 를 보면 좋을 것 같습니다.)
DispatchQueue.main().async{}
  • Global Queue : Global Queue는 Concurrent의 특성을 가져서 작업이 분산되어 처리됩니다.
DispatchQueue.global().async{}
DispatchQueue.global().sync{}

QOS

Quality of Service, QOS는 Global Dispatch Queue 에 추가할 수 있는데 작업의 중요도를 설정해서 우선순위를 정할 수 있습니다. 우선순위가 높은 작업은 우선순위가 낮은 작업보다 더 빨리 수행되기 때문에 앱이 수행하는 작업에 적합한 QoS클래스를 정확하게 지정하면, 앱의 성능이 더 좋아진다고 합니다.
( 우선순위가 높은 순으로 정리했으며 공식문서 참고했습니다. )

  • userInteractive : 사용자와 상호작용하는 작업, 애니메이션 처리, UI 업데이트 등의 경우이며 사용자의 요청에 바로 작업이 수행되어야 해서 가장 높은 우선순위를 가집니다.
    : DispatchQueue.global(qos: .userInteractive) (즉시)
  • userInitiated : 사용자가 요청을 했을 때 결과를 즉각적으로 받아야 하거나, 사용자가 앱을 사용하는 것을 막는 작업에 할당합니다. 예를 들어, 이메일의 내용을 로드하거나 앱 내에서 첨부파일을 열기, 내부 데이터베이스 조회 등에 사용할 수 있습니다.
    : DispatchQueue.global(qos: .userInitiated) (몇초)
  • default : 기본값이며, 일반적인 작업인 경우 사용합니다.
    : DispatchQueue.global()
  • utility : 사용자가 앱을 계속 사용하는 것을 방해하지 않는 작업에 할당합니다. 예를 들어 사용자가 진행 상황을 적극적으로 따르지 않는 작업, 사용자와 상호작용하지 않는 작업에 사용할 수 있습니다. ex : progress bar, 지속적인 데이터 feed, Networking
    : DispatchQueue.global(qos: .utility) (몇초 ~ 몇분)
  • background : 백그라운드 작업은 모든 작업 중에서 우선 순위가 낮습니다. 백그라운드에서 작동하는 작업에 사용할 수 있습니다.
    : DispatchQueue.global(qos: .background)

Serial + Sync

Sync로 설정했으니 코드가 실행될 때 Serial Dispatch Queue에 작업을 하나 등록하면, 해당 작업이 완료될 때 까지 기다립니다. (main.sync 는 사용을 못하니 주의, 등록순서, 출력순서 일치 o)

Concurrent + Sync

Sync로 설정했으니 큐에 등록되는 작업들은 여러 스레드에 분산되어 처리되는데, 앞에 작업이 완료 될 때까지 대기합니다. (등록순서, 출력순서 일치 o)

Serial + Async

Async로 설정했으니 작업의 등록은 앞의 작업 완료여부와 상관없이 계속 진행하고, 작업의 수행은 순차적으로 진행합니다. (등록순서, 출력순서 일치 o)

Concurrent + Async

Async로 설정했으니 작업을 완료 여부와 상관없이 계속 등록하고, 등록된 작업들은 스레드에 분산되어서 동시에 실행됩니다. (등록순서, 출력순서 일치 x)


추가로 주의해야하는 상황

  1. 메인 큐에서 다른 큐로 보낼 때 sync를 사용하면 안된다.
    : 메인 큐에서 다른 큐로 보내서 작업하는 이유는 메인에서 동작하는 작업과 동시적으로 수행하여 속도를 향상시키기 위함인데, sync를 사용하면 해당 작업이 끝날때까지 메인 쓰레드를 block 시키기 때문에 앱이 버벅이게 된다.
  2. 현재 큐에서 같은 큐로 sync를 사용하면 안된다.
    : 실행되고 있던 쓰레드에서 현재 큐로 sync로 작업을 보내면 실행중이던 쓰레드는 block 상태가 된다.

0개의 댓글