동시성 프로그래밍에서 동기와 비동기의 차이, iOS에서 비동기 작업 처리 방법, 세마포어와 뮤텍스의 차이
| 동기 | 비동기 | |
|---|---|---|
| 블로킹 | 예 (blocking) | 아니오 (non-blocking) |
| 호출 반환 | 작업 완료 후 반환 | 즉시 반환 |
| 에러 처리 | 예외/리턴값 | 콜백 인자/Task 재시도 등 |
| 사용 예 | 간단한 로직 순차 처리 | 네트워크 통신, 디스크 I/O |
DispatchQueue.global().async { /* 백그라운드 작업 */ }DispatchQueue.main.async { /* UI 업데이트 */ }DispatchQueue.global(qos: .userInitiated).async {
let image = downloadImage(from: url)
DispatchQueue.main.async {
imageView.image = image
}
}
Operation을 상속하거나 블록(OperationBlock)을 사용let queue = OperationQueue()
let op = BlockOperation {
let data = fetchData()
OperationQueue.main.addOperation {
textView.text = data
}
}
queue.addOperation(op)
dataTask(with:completionHandler:) 또는 async/await 버전URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
parseAndDisplay(data)
}
}.resume()
Task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decoded = try JSONDecoder().decode(Model.self, from: data)
await MainActor.run {
updateUI(with: decoded)
}
} catch {
print(error)
}
}
async/await, Task, Actor)Task.detached vs Task {}@MainActor 또는 await MainActor.run {} 으로 메인 스레드 보장func fetchImage() async throws -> UIImage {
let (data, _) = try await URLSession.shared.data(from: url)
guard let img = UIImage(data: data) else { throw URLError(.badServerResponse) }
return img
}
Task {
do {
let image = try await fetchImage()
imageView.image = image
} catch {
print(error)
}
}
wait() 시 카운터를 하나 줄이고 0보다 작아지면 블록signal() 시 카운터를 하나 늘리고 블록된 스레드를 깨움let sema = DispatchSemaphore(value: 2) // 최대 2개 동시 진입
DispatchQueue.global().async {
sema.wait()
// 임계 구역
sema.signal()
}
NSLock 사용):let lock = NSLock()
DispatchQueue.global().async {
lock.lock()
// 임계 구역
lock.unlock()
}
| 세마포어 | 뮤텍스 | |
|---|---|---|
| 카운터 | 0 이상 정수 (동시 진입 수 제어) | 이진 (1 또는 0) |
| 소유권 | 없음 | 있음 (소유 스레드) |
| 용도 | 자원 풀(pool) 제어, 동시성 제한 | 임계 구역 배타적 접근 |
| unlock 제한 | 어떤 스레드든 signal 가능 | 오직 락 건 스레드만 unlock |
정리