본문 바로가기
iOS

[iOS] Dispatch Queue

by seongminmon 2024. 7. 18.

Dispatch Queue란?

앱의 메인 스레드 또는 백그라운드 스레드에서 연속적으로 또는 동시에 작업 실행을 관리하는 객체

 

DispatchQueue | Apple Developer Documentation

An object that manages the execution of tasks serially or concurrently on your app's main thread or on a background thread.

developer.apple.com

 

Dispatch Queue의 종류

1. main

DispatchQueue.main의 경우 Serial Queue가 기본
작업을 여러 스레드로 분산하지 않고, 하나의 스레드(메인 스레드)에서 처리
UI 업데이트 시, 메인 스레드에 구현해야 함

 

2. global

DispatchQueue.global의 경우 Concurrent Queue가 기본
작업을 여러 스레드로 나눠보냄
작업 순서에 대한 우선 순위(QoS)를 설정할 수 있음 (기본값은 default)

 

QoS의 우선 순위

  1. userInteractive
  2. userInitiated
  3. `default`
  4. utility
  5. background
 

DispatchQoS.QoSClass | Apple Developer Documentation

Quality-of-service classes that specify the priorities for executing tasks.

developer.apple.com

 

3. custom

기본값은 Serial. concurrent로 변경 가능

 

Serial(직렬) / Concurrent(동시)

1. Serial
Queue에 들어온 작업을 하나의 스레드에만 분산하여 처리
순차적으로 진행
순서 보장 O

 

2. Concurrent
Queue에 들어온 작업을 여러 스레드에 분산하여 처리
동시에 여러 작업이 수행될 수 있음
순서 보장 X

 

Sync(동기) / Async(비동기)

 

1. Sync
시작한 작업이 완료되어야 다음 작업을 시작할 수 있음
어떤 작업이 수행중이라면 다음 작업은 대기해야 함
작업 순서가 보장됨
결과의 순서가 중요한 경우에는 동기 코드로 작성해야 함

 

2. Async
시작한 작업이 완료되지 않더라도 그 다음 작업을 시작함
한번에 여러 작업을 진행할 수 있음
먼저 실행된 작업이 먼저 끝나는 것은 아님
작업 순서가 보장되지 않음
결과의 순서가 중요하지 않은 경우 비동기 코드로 작성하는 것이 효율적

 

Concurrency Programming

예시로 사용할 코드

print("Start")
for i in 1...10 {
    print(i, terminator: " ")
}
print("End")

Start
1 2 3 4 5 6 7 8 9 10 End

 

1. Serial / Sync

print("Start")
for i in 1...10 {
    DispatchQueue.main.sync {
        print(i, terminator: " ")
    }
}
print("End")

 

 

Start 출력 후 런타임 에러 발생

메인 스레드에서 DispatchQueue.main.sync를 사용하게 되면 무한 대기 상태에 들어갈 수 있기 때문에 DeadLock(교착 상태)이 발생할 수 있음

 

Serial한 커스텀 Queue를 만들어 실행

let customQueue = DispatchQueue(label: "custom")
print("Start")
for i in 1...10 {
    customQueue.sync {
        print(i, terminator: " ")
    }
}
print("End")

Start
1 2 3 4 5 6 7 8 9 10 End

 

들어온 작업의 순서대로 실행됨

 

 

2. Serial / Async

print("Start")
for i in 1...10 {
    DispatchQueue.main.async {
        print(i, terminator: " ")
    }
}
print("End")

Start
End
1 2 3 4 5 6 7 8 9 10

 

10개의 작업을 비동기적으로 보냈기 때문에 End가 먼저 출력됨
보내진 작업들은 Serial Queue이므로 순서대로 동작

 

3. Concurrent / Sync

print("Start")
for i in 1...10 {
    DispatchQueue.global().sync {
        print(i, terminator: " ")
    }
}
print("End")

Start
1 2 3 4 5 6 7 8 9 10 End

 

global Queue를 Sync로 동작하는 것은 다른 스레드로 작업을 동기적으로 보내는 것을 의미
메인 스레드에서 동작하는 것과 유사하기 때문에, 실질적으로는 메인 스레드에서 작업을 수행하게 됨

 

4. Concurrent / Async

print("Start")
for i in 1...10 {
    DispatchQueue.global().async {
        print(i, terminator: " ")
    }
}
print("End")

Start
1 2 3 4 6 7 End
5 8 9 10

Start
1 3 2 4 6 5 8 7 9 End
10

Start
1 3 4 2 5 6 End
7 8 9 10

 

실행할 때마다 결과가 달라짐
10개의 작업을 비동기적으로 보내고, 동시에 수행되어 순서가 보장되지 않음