핵심
Swift는 언어 차원 지원으로 많은 race를 컴파일 단계에서 차단하고, 일부 런타임 race는 실행 중단으로 보호한다.
Swift 공식 Concurrency 챕터의 흐름을 빠짐없이 따라가며, 각 개념을 실무 관점으로 쉽게 풀어쓴 학습 문서다.
핵심
Swift는 언어 차원 지원으로 많은 race를 컴파일 단계에서 차단하고, 일부 런타임 race는 실행 중단으로 보호한다.
스레드와 관계
모델은 스레드 위에서 동작하지만, 개발자는 스레드를 직접 조작하지 않아도 된다. async 함수는 스레드를 양보할 수 있다.
func listPhotos(inGallery name: String) async -> [String] { ... }
let names = await listPhotos(inGallery: "Summer Vacation")중요: 동기 코드에서 async를 안전하게 감싸 "기다리는" 범용 우회는 제공되지 않는다(교착/경쟁 위험).
`Task.sleep(for:tolerance:clock:)`은 현재 task를 일정 시간 중단해 concurrency 동작을 학습/시뮬레이션할 때 유용하다.
func listPhotos(inGallery name: String) async throws -> [String] {
try await Task.sleep(for: .seconds(2))
return ["IMG001", "IMG99", "IMG0404"]
}for try await line in handle.bytes.lines {
print(line)
}순차 (`await` 연속)
앞 결과가 필요할 때 사용. 호출 하나가 끝난 후 다음 호출로 진행.
병렬 (`async let`)
결과를 나중에 한 번에 쓰면 유리. 시작은 동시에, 소비 시점에 `await`.
async let a = downloadPhoto(named: photoNames[0])
async let b = downloadPhoto(named: photoNames[1])
let photos = await [a, b]let photos = await withTaskGroup(of: Data.self) { group in
for name in photoNames { group.addTask { await downloadPhoto(named: name) } }
var results: [Data] = []
for await p in group { results.append(p) }
return results
}@MainActor
func show(_ photo: Data) { ... }
func downloadAndShowPhoto(named name: String) async {
let photo = await downloadPhoto(named: name)
await show(photo)
}actor TemperatureLogger {
var measurements: [Int]
private(set) var max: Int
...
}struct TemperatureReading: Sendable {
var measurement: Int
}
@available(*, unavailable)
extension FileDescriptor: Sendable {}Q. async면 자동으로 빨라지나?
A. 아니다. 블로킹 완화는 되지만 디코드/렌더/알고리즘 비용은 별도 최적화가 필요하다.
Q. actor만 쓰면 race가 끝나나?
A. 공유 상태 race는 줄지만 취소/재진입/MainActor 경계/리소스 정리는 별도 설계가 필요하다.