Concurrency
Concurrency는 '동시에 여러 작업을 처리하는 것'을 말한다. 예를 들어, 음악 스트리밍 앱에서 노래를 들으면서 동시에 카톡을 확인할 수 있는 것처럼, 앱이 여러 작업을 한 번에 처리할 수 있도록 만들어 주는 것이다.
Concurrency를 사용하면, 앱이 응답성이 높아지고 사용자가 느끼는 딜레이를 최소화할 수 있다. Swift에서는 이를 간편하게 구현할 수 있는 다양한 도구를 제공한다.
Thread
쓰레드는 프로세스 내에서 실행되는 작업 단위이다. 프로세스가 한 개의 메인 쓰레드를 가지고 있다면, 앱의 모든 작업이 그 메인 쓰레드에서 실행된다. 하지만 네트워크 요청과 같은 무거운 작업을 메인 쓰레드에서 처리하면 앱이 멈추거나 느려질 수 있다.
그래서 추가적인 쓰레드를 생성하거나 백그라운드에서 작업을 처리함으로써 성능을 개선할 수 있다.
GCD(Grand Central Dispath)
GCD는 Apple에서 제공하는 저수준의 Concurrency API로, 여러 작업을 효율적으로 관리하고 실행할 수 있도록 도와준다. GCD는 작업을 Queue에 넣어서 처리한다.
DispatchQueue
DispatchQueue는 작업을 정리하고 실행 순서를 관리해 주는 도구이다.
Serial Queue (직렬 큐)
이름 그대로 작업을 한 번에 하나씩, 순서대로 실행.
작업들이 서로 독립적이거나 순서를 엄격히 지켜야 하는 경우에 유용함.
기본적으로 생성한 Custom Queue는 Serial Queue로 동작함.
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
serialQueue.async {
print("작업 1")
}
serialQueue.async {
print("작업 2")
}
// 출력: 항상 "작업 1", 그다음 "작업 2" 순서대로 실행
Concurrent Queue (병렬 큐)
여러 작업을 동시에 실행할 수 있음.
작업 순서는 보장되지 않지만, 모든 작업이 완료됨
DispatchQueue.global()
을 통해 미리 정의된 Global Concurrent Queue를 사용할 수 있음.
let concurrentQueue = DispatchQueue.global()
concurrentQueue.async {
print("작업 1")
}
concurrentQueue.async {
print("작업 2")
}
// 출력: "작업 1"과 "작업 2"가 순서에 상관없이 동시에 실행될 가능성이 높다.
Custom Queue
개발자가 직접 정의한 Queue로, 일반적으로 Serial Queue로 동작하지만 Concurrent Queue로도 설정할 수 있다.
Custom Serial Queue
let mySerialQueue = DispatchQueue(label: "com.example.mySerialQueue") mySerialQueue.async { print("커스텀 직렬 작업 1") } mySerialQueue.async { print("커스텀 직렬 작업 2") }
Custom Concurrent Queue
Custom Concurrent Queue를 생성하려면
attributes
를 사용하여 명시적으로 설정해야 함.
let myConcurrentQueue = DispatchQueue(label: "com.example.myConcurrentQueue", attributes: .concurrent) myConcurrentQueue.async { print("커스텀 병렬 작업 1") } myConcurrentQueue.async { print("커스텀 병렬 작업 2") }
Main Queue
앱의 메인 쓰레드에서 작업을 실행함.
UI와 관련된 작업은 반드시 메인 쓰레드에서 처리해야 함.
Main Queue는 Serial Queue로 동작함.
DispatchQueue.main.async {
print("메인 쓰레드에서 실행되는 작업")
}
Global Queue
Concurrent Queue로, 시스템에서 미리 정의한 Queue.
QoS(Quality of Service)에 따라 우선순위 설정 가능.
.userInteractive
: UI와 관련된 매우 중요한 작업.userInitiated
: 사용자가 요청한 작업.utility
: 오래 걸리는 작업(예: 다운로드).background
: 덜 중요한 작업(예: 데이터 정리)
DispatchQueue.global(qos: .background).async {
print("백그라운드 작업")
}
Serial Queue는 순차적으로 처리해야 하는 작업에 적합하다. 예를 들어, 파일을 하나씩 저장하거나 데이터베이스 업데이트 같은 작업들.
Concurrent Queue는 병렬로 처리가 가능한 작업에서 빛을 발한다. 예를 들어, 이미지 다운로드나 데이터 분석처럼 서로 의존성이 없는 작업들.
Async
async
는 비동기 작업을 처리하는데 사용되는 기능이다. 함수 또는 작업이 비동기로 실행되며, 호출 시 작업이 완료되길 기다리지 않고 다음 코드를 실행한다. 앱의 응답성을 유지하면서 동시에 여러 작업을 처리하는 데 유용하다.
func fetchData() async -> String {
// 데이터를 비동기로 가져오는 함수
return "비동기 작업 완료"
}
Task {
let result = await fetchData()
print(result) // 출력: 비동기 작업 완료
}
Await
awiat
은 async
함수나 작업의 결과가 준비될 때까지 기다리는 데 사용된다. 비동기 작업이 끝날 때까지 해당 지점에서 잠깐 멈추지만, 메인 스레드를 블로킹하지는 않는다.
func fetchData() async -> String {
return "비동기 데이터"
}
func processData() async {
let data = await fetchData() // fetchData 작업이 끝날 때까지 대기
print("받은 데이터: \(data)")
}
Task {
await processData()
}
Sync
sync
는 비동기와 반대되는 개념으로, 작업이 완료될 때까지 코드 실행을 멈춘다. 즉, 다른 작업이 동시 실행되지 못하고 대기하게 된다. Swift의 Concurrency에서는 기본적으로 sync
를 사용하지 않는 것이 좋지만 상황에 따라 필요한 경우가 있을 수 있다.
let queue = DispatchQueue(label: "com.example.syncQueue")
queue.sync {
print("동기 작업 시작")
}
print("동기 작업 완료 후 실행")
Actor
actor
는 여러 스레드가 동일한 데이터에 접근할 때 발생할 수 있는 동시성 문제를 해결하기 위한 도구이다. 데이터를 안전하게 보호하고 작업을 순차적으로 실행한다.
actor Counter {
private var value = 0
func increment() {
value += 1
}
func getValue() -> Int {
return value
}
}
let counter = Counter()
Task {
await counter.increment()
print(await counter.getValue()) // 출력: 1
}
Actor와 Class의 차이점
특징 | Actor | Class |
쓰레드 안전성 | 쓰레드 안전성을 보장하며, 데이터 접근 시 자동으로 동기화 처리 | 쓰레드 안전성이 보장되지 않으며, 동기화는 개발자가 직접 처리 |
동시성 문제 | 여러 쓰레드에서 안전하게 공유 데이터 조작 가능 | 여러 쓰레드에서 동시 접근 시 Race Condition(데이터 경쟁) 문제가 발생할 수 있음 |
데이터 관리 | 내부 데이터 접근은 | 비동기적 처리가 필요 없으며, 기본적으로 동기적으로 접근 가능 |
사용 목적 | 데이터의 동시성 문제가 발생할 가능성이 높은 경우에 사용 | 동시성 문제가 없는 경우 더 간단하고 빠르게 동작 |
Actor는 데이터 경쟁 상태를 방지하기 위해 설계되었으며, 다음과 같은 상황에서 유용하다.
동시에 여러 쓰레드에서 동일한 데이터에 접근하거나 변경해야 하는 경우
데이터의 일관성과 안전성을 보장해야 하는 경우
반대로, 동시성이 필요하지 않는 간단한 데이터 구조에서는 Actor 대신 Class나 Struct를 사용하는 것이 더 효율적일 수 있다.