Background Thread를 사용해야 하는 이유
작업을 할 때 무거운 작업을 하는 경우 CPU를 많이 사용하기 됩니다. 다음은 현재 사용중인 CPU 사용률을 확인하는 방법입니다.
아래에서 첫 번째 Thread인 Thread 1은 메인 스레드이며 대부분의 코드들이 기본적으로 메인 스레드인 Thread 1에서 작업이 됩니다.
Thread 1 즉 메인 스레드에서 작업을 가볍게 하는 경우는 상관 없지만 만약 무거운 작업을 하게 되어 Thread 1 메인 스레드가 포화 상태가 된다면 다른 작업들은 할 수 없게 되며 이게 굉장히 안좋은게 UI 변화 같은 경우도 메인 스레드에서 작업을 하기 때문에 화면이 버벅인다거나 멈출 수 있기 때문입니다.
하지만 CPU는 메인 스레드 외에도 여러개의 스레드를 만들어 낼 수 있습니다. (프로세스는 여러개의 스레드로 이루어져 있습니다)
다음은 백그라운드 스레드에서 데이터를 가져와 메인 스레드에서 UI를 바꾸는 코드입니다.
@Observable class BackgroundThreadViewModel {
var dataArray: [String] = []
func fetchData() {
// 백그라운드 스레드에서 다운로드하기
DispatchQueue.global().async {
let newData = self.downloadData()
// 메인 스레드에서 UI 변경하기
DispatchQueue.main.async {
self.dataArray = newData
}
}
}
func downloadData() -> [String] {
var data: [String] = []
for x in 0..<100 {
data.append("\(x)")
print(data)
}
return data
}
}
struct ContentView: View {
@Environment(BackgroundThreadViewModel.self) var vm
var body: some View {
ScrollView {
VStack(spacing: 10) {
Text("LOAD DATA")
.font(.largeTitle)
.fontWeight(.semibold)
.onTapGesture {
vm.fetchData()
}
ForEach(vm.dataArray, id: \.self) { item in
Text(item)
.font(.headline)
.foregroundStyle(.red)
}
}
}
}
}
DispatchQueue.global
global은 다음과 같이 qos(우선순위) 여러개의 종류를 선택할 수 있습니다.
qos란? DispatchQueue에서 수행 할 작업을 분류하기 위해 사용됩니다. 또한 시스템이 우선순위를 정하고 이에 따라서 스케줄링을 하게 됩니다.
DispatchQueue.global(qos: .userInteractive) {} //Main Queue처럼 UI 변화, Main thread는 아닙니다
DispatchQueue.global(qos: .userInitiated) {} //유저가 시작한 작업, 유저가 작이 완료될 때까지 작업 X
DispatchQueue.global(qos: .background) {} //유저가 알아차리지 못하는 작업
DispatchQueue.global(qos: .default) {} //userInitiated와 utility의 중간
DispatchQueue.global(qos: .utility) {} //시간이 걸리며 즉각적인 응답이 필요하지 않은 경우
DispatchQueue.global(qos: .unspecified) {}
실험해보기
다음은 버튼을 누르게 됐을 때 fetchData를 통해서 데이터를 가져오고 그 데이터를 화면에 띄우는 작업입니다.
@Observable class BackgroundThreadViewModel {
var dataArray: [String] = []
func fetchData() {
// 백그라운드 스레드에서 다운로드하기
DispatchQueue.global(qos: .background).async {
let newData = self.downloadData()
// 메인 스레드에서 UI 변경하기
DispatchQueue.main.async {
self.dataArray = newData
}
}
}
func downloadData() -> [String] {
var data: [String] = []
for x in 0..<100 {
data.append("\(x)")
print(data)
}
return data
}
}
struct ContentView: View {
@Environment(BackgroundThreadViewModel.self) var vm
var body: some View {
ScrollView {
LazyVStack(spacing: 10) {
Text("LOAD DATA")
.font(.largeTitle)
.fontWeight(.semibold)
.onTapGesture {
vm.fetchData()
}
ForEach(vm.dataArray, id: \.self) { item in
Text(item)
.font(.headline)
.foregroundStyle(.red)
}
}
}
}
}
위에 CPU 사용량을 확인하였을 때 알 수 있는 것은 데이터를 가져올 때는 Thread 12와 Thread 13을 사용하였고 UI의 변화를 적용하는 데에는 메인 스레드인 Thread1의 사용량이 올라갔음을 확인할 수 있었습니다.
MainThread에서 작업 하는지 확인하는 방법
Thread.isMainThread 또는 Thread.current 를 사용하여 현재 작업이 메인 스레드에서 진행되는지, 진행되고 있는 스레드가 무슨 스레드인지를 확인할 수 있습니다.
func fetchData() {
// 백그라운드 스레드에서 다운로드하기
DispatchQueue.global(qos: .background).async {
let newData = self.downloadData()
print("CHECK 1: \(Thread.isMainThread)")
print("CHECK 1: \(Thread.current)")
// 메인 스레드에서 UI 변경하기
DispatchQueue.main.async {
self.dataArray = newData
print("CHECK 2: \(Thread.isMainThread)")
print("CHECK 2: \(Thread.current)")
}
}
}
'SwiftUI' 카테고리의 다른 글
Weak Self (0) | 2024.04.07 |
---|---|
CoreBluetooth로 아두이노 불 켜기 (1) | 2024.04.07 |
버튼 커스텀화하기, Custom ButtonStyle (0) | 2024.04.07 |
Custom ViewModifiers (0) | 2024.04.07 |
@FocusState 로 키보드 다루기 (0) | 2024.04.06 |