DispatchQueue
- 스위프트에서 멀티스레드 프로그래밍을 관리하기 위해서 사용하는 기본도구 중 하나
- 작업을 비동기적으로 실행하거나 지연시킬 수 있는 queue를 제공합니다. 이 큐를 통해서 스레드 간의 작업을 관리하고 동시성 문제를 해결할 수 있습니다.
디스패치 큐는 크게 2가지 종류로 나뉩니다.
Main Queue
메인 스레드에서 작업을 실행하는 큐입니다. 주로 UI 업데이트와 같이 메인 스레드에서 실행되어야하는 작업을 처리합니다.
Global Queue
시스템에서 제공하는 전역 큐입니다. 여러가지 우선순위(QOS : quality of service)를 갖는 여러가지 큐들이 있습니다. 이 큐들은 백그라운드에서 작업을 처리할 때 주로 사용됩니다.
사용
Main Queue
메인큐는 주로 UI업데이트를 위해서 사용되며, 모든 UI작업은 메인스레드에서 실행되어야 하므로, 네트워크 요청이나 무거운 작업이 끝난 후 UI 업데이트를 할 때는 반드시 메인큐에서 실행되어야 합니다.
DispatchQueue.main.async{
self.label.text = "Data Loaded"
}
Global Queue
전역 큐는 백그라운드 작업을 처리할 때 사용됩니다. 예를 들어서, 네트워크 요청, 파일 읽/쓰, 이미지 처리와 같은 작업들은 전역 큐에서 처리하여 UI 및 간단한 작업을 담당하고 있는 메인스레드를 블로킹하지 않도록 합니다.
DispatchQueue.global(qos : .background).async {
let data = self.loadDataFromNetwork()
DispatchQueue.main.async{
self.updateUI(with:data)
}
}
동기작업 (순차적)
동기작업 (synchronous) 방식은 주로 큐 내에서 작업이 순차적으로 실행되어야 할 때 사용됩니다.
하지만, 메인 큐에서 sync를 사용할 때는 데드락을 피하기 위해서 매우 신중해야 합니다.
DispatchQueue.global().sync {
// 이 코드는 현재 스레드를 블로킹하고 순차적으로 실행됩니다.
// 주의: 메인 큐에서는 sync를 사용하지 않도록 주의.
print("This is a synchronous task")
}
비동기작업 (동시적)
비동기작업 (asynchronous) 방식은 일반적으로 스레드 블로킹을 피하기 위해서 사용됩니다.
DispatchQueue.global().async {
// 이 코드는 비동기적으로 실행됩니다.
print("This is an asynchronous task")
}
DispatchQueue 활용 예시
백그라운드에서 데이터 로드 후 UI 업데이트
func loadDataAndUpdateUI(){
DispatchQueue.global(qos: .userInitiated).async{
// 백그라운드 작업 : 데이터 로드
let data = self.loadDataFromNetwork()
DispatchQueue.main.async{
// 메인스레드에서 UI 업데이트
self.label.text = data
}
}
}
작업을 일정시간동안 지연시킬 수도 있습니다.
DispatchQueue.main.asyncAfter(deadline : .now() + 2.0){
// 2초 후에 실행
self.label.text = "This Text appears after a delay"
}
DispatchQueue와 async-await의 비교
DispatchQueue와 async/await은 모두 비동기 프로그래밍을 처리하는 방법입니다.
그러나 DispatchQueue는 비교적 더 로우 레벨의 작업 큐를 직접 관리하는 방식이고,
async await은 보다 고수준의 추상화된 방법으로 비동기 작업을 처리하는데 더 직관적입니다.
UI Kit에서 주로 사용되는 비동기 작업의 경우, DispatchQueue와 async-await을 적절히 조합하여 사용할 수 있습니다.
func loadDataAndUpdateUI() async{
let data = await loadDataFromNetwork()
DispatchQueue.main.async{
self.label.text = data
}
}
QOS Quality Of Service의 종류
QOS는 작업의 우선순위를 정의하는 방식입니다.
시스템이 작업을 언제 그리고 얼마나 빨리 실행할지를 결정하는 것에 도움을 줍니다.
Swift에서는 DispatchQoS.QoSClass 열거형(enum)을 사용하여 여러 종류의 QOS를 제공합니다.
각 QOS는 작업이 얼마나 중요한지를 나타내며, 다음과 같은 종류가 있습니다.
- userInteractive
사용자와의 직접적인 상호작용이 필요한 작업에 사용됩니다.
예를 들어서, 애니메이션, 터치 이벤트 처리 등이 여기에 해당됩니다.
가장 높은 우선순위를 갖습니다.
- userInitiated
사용자가 명시적으로 시작한 작업에 사용됩니다.
빠른 응답이 필요하지만, 약간의 지연이 허용되는 작업에 적합합니다.
예를 들어서, 사용자가 버튼을 눌렀을 때 데이터를 로드하는 작업 등이 있습니다.
- utility
오랜 시간이 걸릴 수 있는 작업에 사용됩니다.
예를 들어서, 파일 다운로드나 데이터 백업 작업이 여기에 해당합니다.
사용자에게 즉시 피드백을 제공할 필요는 없지만, 실행히 완료되기를 원하는 작업입니다.
- background
사용자에게 보이지 않는 작업에 사용됩니다.
예를 들어서, 데이터 동기화, 미리 다운로드 등의 작업이 해당됩니다.
가장 낮은 우선순위를 가집니다.
- default
기본 우선순위입니다.
다른 우선순위를 지정하지 않으면 이 QOS가 사용됩니다.
- unspecified
우선순위를 명시하지 않을 때 사용됩니다. 시스템에 의해서 적절한 우선순위가 자동으로 할당됩니다.
추가로,,,
qos를 지정할 때
DispatchQueue.global(qos: .userInitiated)
이런식으로 지정하고 있는데
.userInitiated에서 앞에 .은 갑자기 튀어나와서 이게 뭔지 싶을 수 있습니다.
이것은 열거형인데, 명시적으로 알려진 경우에는 앞의 슈퍼셋들을 생략할 수 있습니다.
명시적으로 알려진 건지 어떻게 아느냐
스위프트에서 열거형 enum 타입의 값을 .userInitiated와 같이 .으로 간단히 표현할 수 있는 이유는 컴파일러가 타입을 추론할 수 있기 때문입니다.
타입 추론이 가능하려면 코드의 문맥 context에서 사용되는 타입이 명확하게 결정되어야 합니다.
스위프트에서의 타입 추론
스위프트 컴파일러는 문맥에서 필요한 타입을 추론할 수 있습니다.
즉 어떤 변수나 함수의 파라미터 타입이 이미 명확하게 정의되어 있는 경우, 그 변수나 파라미터에 할당되는 값의 타입도 자연스럽게 추론할 수 있습니다.
예시 1. 명시적인 타입지정
let qos : DispatchQoS.QoSClass = .userInitiated
여기서 qos의 타입이 DispatchQoS.QoSClass로 명시되어 있기 때문에, 컴파일러는 .userInitiated가 DispatchQoS.QoSClass의 값이라는 것을 알고 있습니다.
예시 2. 함수 파라미터로 타입이 명확한 경우
func setQoS(_ qos: DispatchQoS.QoSClass){
// 여기서 qos는 DispatchQos.QoSClass 타입으로 지정됩니다.
}
setQoS(.userInitiated)
setQoS 함수의 파라미터가 DispatchQoS.QoSClass 타입으로 명확하게 정의되어 있기 때문에, setQoS(.userInitiated)에서 .userInitiated를 사용하는 것이 가능합니다.
이경우에 컴파일러는 setQoS 함수의 파라미터 타입을 기반으로 .userInitiated가 어떤 열거형의 값인지 추론합니다.
타입이 명확하게 결정되는 경우들은 다음과 같습니다.
1. 변수나 상수 선언 시에 명시적으로 타입이 지정된 경우
let qos: DispatchQoS.QoSClass = .userInitiated
2. 함수나 메서드의 파라미터 타입이 명확한 경우
func setQoS(_ qos: DispatchQoS.QoSClass){
}
setQoS(.userInitiated)
3. 타입 캐스팅을 통해서 특정 타입이 확정된 경우
let qos = someFunctionReturningAny() as? DispatchQoS.QoSClass
if qos == .userInitiated{
}
4. 열거형의 타입이 이미 명확한 경우 ( 열거형 값이 특정 열거형 타입의 일부로 사용될 때 )
let qosClass : DispatchQoS.QoSClass = .userInitiated
let anotherQoS = DispatchQoS.QoSClass.userInteractive
'Ios' 카테고리의 다른 글
클로저에 대해서 알아보자 (0) | 2024.08.16 |
---|---|
self에 대해서 알아보자 (0) | 2024.08.16 |
struct 그리고 class (0) | 2024.08.16 |
as에 대해서 알아보자 (0) | 2024.08.16 |
async-await / try (0) | 2024.08.16 |