티스토리 뷰
[iOS] GCD - QoS, DispatchWorkItem, DispatchGroup 제대로 알고 쓰자! (2)
lsh424 2021. 4. 13. 15:56[iOS] GCD - Dispatch Queue 제대로 알고 쓰자! (1) 에서 이어지는 글입니다 :)
QOS
A quality of service (QoS) class allows you to categorize work to be performed by NSOperation, NSOperationQueue, NSThread objects, dispatch queues, and pthreads (POSIX threads). By assigning a QoS to work, you indicate its importance, and the system prioritizes it and schedules it accordingly. For example, the system performs work initiated by a user sooner than background work that can be deferred until a more optimal time. In some cases, system resources may be reallocated away from the lower priority work and given to the higher priority work.
QoS 클래스를 사용하면 NSOperation, NSOperationQueue, NSThread 객체, 디스패치 큐 및 pthread (POSIX 스레드)에서 수행할 작업을 분류할 수 있습니다. 작업에 QoS를 할당하여 중요성을 나타내면 시스템이 우선순위를 지정하고 그에 따라 스케줄링합니다. 예를 들어, 시스템은 더 최적의 시간까지 연기할 수 있는 백그라운드 작업보다 더 빨리 사용자가 초기화한 작업을 수행합니다. 경우에 따라 시스템 리소스는 우선순위가 낮은 작업에서 우선순위가 높은 작업에 재할당 될 수 있습니다.
...
위 문서에 들어가보면 QoS에 대한 설명이 자세히 나와있는데요, 한마디로 qos는 중요성? 우선순위?를 지정하는 클래스라고 보시면 될 것 같습니다. 그렇기 때문에 우선순위가 높은 작업은 우선순위가 낮은 작업보다 더 빨리 수행됩니다. (하지만 그만큼 리소스와 더 많은 에너지가 필요합니다. ~~ 앱이 수행하는 작업에 적합한 QoS 클래스를 정확하게 지정하면 앱이 응답 성과 에너지 효율성을 보장합니다.)
QoS에는 다음과 같이 크게 4 개의 기본 QoS 클래스로 나눌 수 있습니다.
1. userInteractive: 메인스레드에서 동작하는 유저와 인터렉팅 하는 작업, UI, animation.. 등등 즉, 반응성과, 성능에 초점을 둡니다.
2. userInitiated: 문서를 열거 나 사용자가 사용자 인터페이스에서 무언가를 클릭 할 때 작업을 수행하는 것과 같은 즉각적인 결과가 필요한 작업, 응답 성과 성능에 중점을 둡니다.
3. utility: 완료하는 데 다소 시간이 걸릴 수 있으며 데이터 다운로드 또는 가져오기와 같이 즉각적인 결과가 필요하지 않은 작업, 응답성, 성능 및 에너지 효율성 간의 균형을 제공하는 데 중점을 둡니다.
4. background: 색인 생성, 동기화 및 백업과 같이 백그라운드에서 작동하고 사용자에게 표시되지 않는 작업입니다. 에너지 효율성에 중점을 둡니다.
이 네가지 기본 QoS 클래스 외에도 두 가지 특수 QoS 유형이 있습니다.
1. default: 우리가 따로 qos를 지정하지 않을 때 사용되는 qos클래스로, 이 QoS의 우선 순위 수준은 userInitiated와 utility 사이에 있습니다.
2. unspecified: QoS 정보가 없음을 나타내며 시스템에 QoS를 추론해야 한다는 신호를 보냅니다.
아까 커스텀 큐 말고도 글로벌 큐에서 qos를 지정할 수 있다고 했는데, 다음과 같이 구분해서 사용하시면 됩니다.
테스트를 해봅시다!
먼저 다음과 같이 두 개의 Serial 큐를 만들고 qos를 지정해주었는데요. queue1의 qos를 queue2의 qos보다 더 높게 설정해주었습니다.
let queue1 = DispatchQueue(label: "queue1", qos: .userInitiated)
let queue2 = DispatchQueue(label: "queue2", qos: .utility)
queue1.async {
for _ in 1...5 {
print("queue1")
}
}
queue2.async {
for _ in 1...5 {
print("queue2")
}
}
이렇게 실행을 해보면 다음과 같이 async임에도 queue1이 대부분, 먼저 실행되는 것을 볼 수 있습니다.
만약 다음과 같이 두개의 큐에 같은 qos를 지정해주면 비슷한 빈도수로 출력되는 것을 확인할 수 있습니다.
let queue1 = DispatchQueue(label: "queue1", qos: .utility)
let queue2 = DispatchQueue(label: "queue2", qos: .utility)
global 큐(Concurrent Queue) 역시 qos에 따라 serial 큐와 비슷한 결과를 보여줍니다. ㅎㅎ
DispatchQueue.global(qos: .userInitiated).async {
for _ in 1...5 {
print("task11111")
}
}
DispatchQueue.global(qos: .background).async {
for _ in 1...5 {
print("task22222")
}
}
DispatchWorkItem
보통은 다들 Dispatch Queue를 사용할 때 후행 클로저 블록 안에다가 작업을 정의했을텐데, 이 작업을 DispatchWorkItem을 사용해 따로 정의할 수도 있습니다.
let item1 = DispatchWorkItem(qos: .utility) {
for _ in 1...5 {
print("qos = utility")
}
}
let item2 = DispatchWorkItem(qos: .userInitiated) {
for _ in 1...5 {
print("qos = userInitiated")
}
}
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
queue.async(execute: item1)
queue.async(execute: item2)
이렇게 실행을 해보면 다음과 같이 우선순위가 더 높은 item2가 대부분의 경우에서 먼저 실행되는 것을 볼 수 있습니다.
Dispatch Group
애플 문서를 보면 Dispatch Group을 다음과 같이 한문장으로 심플하게 표현했는데요
A group of tasks that you monitor as a single unit.
개요를 구체적으로 살펴보면 다음과 같습니다.
Groups allow you to aggregate a set of tasks and synchronize behaviors on the group. You attach multiple work items to a group and schedule them for asynchronous execution on the same queue or different queues. When all work items finish executing, the group executes its completion handler. You can also wait synchronously for all tasks in the group to finish executing.
그룹을 사용하면 작업 집합을 집계하고 그룹의 동작을 동기화할 수 있습니다. 여러 작업 항목을 그룹에 연결하고 동일한 큐 또는 다른 큐에서 비동기 실행을 위해 예약합니다. 모든 작업 항목의 실행이 완료되면 그룹은 완료 핸들러를 실행합니다. 그룹의 모든 작업이 실행을 마칠 때까지 동기식으로 기다릴 수도 있습니다.
즉, Dispatch Group은 여러 작업을 모니터링하고 하나로 묶어서(group) 관리할 수 있다!
대표적으로 다음과 같이 사용할 수있습니다.
예를 들면 여러 개의 큐에서 async로 작업을 수행한다고 해봅시다. 만약 모든 작업이 끝난 후에 어떤 작업을 실행하고 싶을 때 Dispatch Group을 사용하면 좋습니다.
let group = DispatchGroup()
let queue1 = DispatchQueue(label: "queue1", attributes: .concurrent)
let queue2 = DispatchQueue(label: "queue2", attributes: .concurrent)
let queue3 = DispatchQueue(label: "queue3", attributes: .concurrent)
queue1.async(group: group) {
for _ in 1...3 {
print("queue1!!")
}
}
queue2.async(group: group) {
for _ in 1...3 {
print("queue2~~")
}
}
queue3.async(group: group) {
for _ in 1...3 {
print("queue3**")
}
}
group.notify(queue: DispatchQueue.main) {
print("모든 큐 작업 완료~")
}
각각의 큐에서 작업을 실행할 때 그룹을 지정할 수 있고, group내의 모든 작업이 끝나게 되면 notify안의 코드가 실행되게 됩니다.
'Swift&iOS > iOS' 카테고리의 다른 글
[iOS] SwiftUI 기초 - 공식 튜토리얼 정리(2) (0) | 2021.04.15 |
---|---|
[iOS] SwiftUI 기초 - 공식 튜토리얼 정리(1) (4) | 2021.04.14 |
[iOS] GCD - Dispatch Queue 제대로 알고 쓰자! (1) (1) | 2021.04.11 |
[iOS] Network - TLS 문제 해결 (0) | 2020.12.19 |
[iOS] MVVM 디자인 패턴 정리 및 예제코드 (11) | 2020.11.17 |