스레드는 프로그램이 동시에 여러 작업을 처리할 수 있게 해주는 중요한 개념입니다. 스레드를 사용하면 프로그램이 여러 일을 동시에 처리할 수 있으며 성능을 크게 향상시킬 수 있습니다.
목차
1.
스레드란?
운영 체제가 프로세스 내에서 실행되는 가장 작은 단위입니다. 모든 프로그램은 최소 하나의 스레드를 가지고 있으며 이를 메인 스레드라고 부릅니다. 스레드는 프로세스 내에서 동시에 여러 작업을 처리할 수 있으며 프로세스가 자원을 여러 스레드 간에 공유하면서 작업을 수행합니다.
프로레스를 ‘회사’라고 생각하면 스레드는 ‘직원’이라고 볼 수 있습니다. 회사는 여러 직원이 각기 다른 작업을 병렬로 수행하게 하여 더 많은 일을 할 수 있습니다. 마찬가지로 프로세스도 여러 스레드를 생성해 다양한 작업을 동시에 처리할 수 있습니다.
싱글 스레드 vs 멀티 스레드
싱글 스레드는 한 번에 하나의 작업만 처리합니다. 모든 작업이 순차적으로 실행되며 이전 작업이 끝나야 다음 작업이 시작됩니다. 싱글 스레드의 단점은 오래 걸리는 작업이 있다면 그 작업이 완료될 때까지 나머지 작업들이 대기하게 된다는 점입니다.
func loadData() {
// 서버에서 데이터를 로드하는 긴 작업
}
func updateUI() {
// UI를 업데이트하는 작업
}
loadData() // 데이터 로드가 끝날 때까지 기다림
updateUI() // 그 후에 UI 업데이트
Swift
복사
멀티스레드는 여러 작업을 동시에 실행할 수 있게 해줍니다. 이를 통해 프로그램은 한 작업이 오래 걸리더라도 다른 작업이 병렬로 진행될 수 있습니다. 멀티 스레드를 사용하면 CPU 코어를 효율적으로 활용해 프로그램 성능을 크게 향상시킬 수 있습니다.
DispatchQueue.global().async {
loadData() // 백그라운드에서 데이터 로드
}
DispatchQueue.main.async {
updateUI() // 메인 스레드에서 UI 업데이트
}
Swift
복사
스레드의 메모리 모델
스레드는 프로세스 내에서 힙 메모리와 같은 자원을 공유합니다. 즉, 여러 스레드가 동일한 메모리 영역을 접근할 수 있습니다. 이 때문에 성능이 향상되지만 동시에 Race Condition(경쟁 조건)이나 데이터 불일치와 같은 문제가 발생할 수 있습니다.
스택 메모리는 스레드마다 별도로 관리되며 지역 변수는 각 스레드의 스택에 저장됩니다.
힙 메모리는 프로세스 내의 모든 스레드가 공유하는 메모리 영역입니다. 동시성 문제는 주로 힙 메모리 접근 시 발생합니다.
Race Condition이란, 멀티 스레드 환경에서 두 개 이상의 스레드가 동시에 동일한 자원에 접근하여 잘못된 동작이나 예기치 않은 결과가 발생할 수 있는 상황을 말합니다(데이터 무결성 훼손). 이는 특히 변경 가능한 자원에 여러 스레드가 동시에 접근할 때 발생할 가능성이 큽니다.
var counter = 0
DispatchQueue.global().async {
for _ in 1...1000 {
counter += 1
}
}
DispatchQueue.global().async {
for _ in 1...1000 {
counter += 1
}
}
Swift
복사
위 코드는 두 개의 스레드가 동시에 counter 변수에 접근해 1,000번씩 더하는 작업을 수행합니다. 논리적으로 counter 값은 2,000이 되어야 합니다. 하지만 동시에 여러 스레드가 counter에 접근하면서 그 결과는 2,000이 아닐 수 있습니다. 이것이 바로 Race Condition입니다.
Grand Central Dispatch(GCD)와 Swift Concurrency
GCD는 Apple의 멀티 스레드 관리 API로, 멀티 스레드를 쉽게 관리할 수 있도록 해줍니다. Swift에서 흔히 사용하는 DispatchQueue는 GCD의 일부입니다.
DispatchQueue.global().async {
// 백그라운드에서 작업
}
DispatchQueue.main.async {
// 메인 스레드에서 작업
}
Swift
복사
Swift 5.5에서 도입된 Swift Concurrency는 멀티 스레드를 더 직관적이고 안전하게 관리할 수 있게 해주는 새로운 비동기 프로그래밍 모델입니다. 이를 통해 async와 await를 사용하여 비동기 작업을 순차적 코드처럼 작성할 수 있습니다.
func fetchData() async -> Data {
// 비동기 네트워크 요청
}
func updateUI() async {
let data = await fetchData()
// UI 업데이트
}
Task {
await updateUI()
}
Swift
복사