swift 공식 문서
[iOS - swift 공식 문서] 17. Concurrency (동시성), async, await
jake-kim
2021. 7. 14. 22:11
* 기초: Sync vs Async vs Serial vs Concurrency 개념
Concurrency
- Swift는 비동기 및 병렬 코드 진행을 지원
- Async코드는 일시 중단되었다가 나중에 다시 시작할 수있지만 한 번에 프로그램의 한 부분만을 실행
- ex) 앱을 일시 중단 후 다시 시작하면 UI업데이트와 같은 작업을 진행하면서 네트워크를 통해 데이터를 가져오는 작업
- swift에서의 Concurrency: await, async
- swift가 아닌 다른 언어에서는 Concurrency를 추가하면 디버그가 더 어렵게 만드는 상황이 발생
- Swift의 언어 수준에서 지원하는 async, await를 사용하면 컴파일 시간에 Concurrency에 관한 문제도 파악 가능
Async 함수 정의 및 호출 (async, await)
- async, await를 사용하지 않은 경우, async 관련 처리는 closure로 처리
- 중첩 closure로, 복잡한 코드 형태
listPhotos(inGallery: "Summer Vacation") { photoNames in
let sortedNames = photoNames.sorted()
let name = sortedNames[1]
downloadPhoto(named: name) { photo in
show(photo)
}
}
- async를 사용한 함수 정의: '->'앞에 async 키워드 입력
func listPhotos(inGallery name: String) async -> [String] {
let result = // ... some asynchronous networking code ...
return result
}
- 사용하는 쪽: await키워드 입력
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[1]
let photo = await downloadPhoto(named: name)
show(photo)
- 실행 순서
- 첫번째 줄에서 await 키워드 함수가 호출되는 동안 대기
- 대기하는 동안 동일한 프로그램의 일부 다른 코드가 동시 실행
- listPhotos(inGallery:)반환된 후 이 코드는 해당 지점에서 시작하여 다시 실행
- await에서 대기하는 것을 'thread를 양보(yielding)'한다고 표현
병렬로 Async 함수 호출
- 병렬이 아닌 async 함수 호출
let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])
let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
- 병렬로 async함수 호출 방법: 'async let' 키워드로 선언
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
Task와 TaskGroup
- Task: 프로그램의 일환이고 async로 실행할 수 있는 작업의 단위
- 각 Task들은 child task들을 갖을 수 있으므로 세부 분류를 위해 TaskGroup 사용
await withTaskGroup(of: Data.self) { taskGroup in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
taskGroup.async { await downloadPhoto(named: name) }
}
}
Actor
- Actor는 reference type이며 한 번에 하나의 작업만 변경 가능한 상태에 액세스할 수 있도록 허용
- 여러 작업의 코드가 actor의 동일한 인스턴스와 상호 작용하는 것에 safe
- 'class'키워드와 유사하게 actor키워드로 정의
actor TemperatureLogger {
let label: String
var measurements: [Int]
private(set) var max: Int
init(label: String, measurement: Int) {
self.label = label
self.measurements = [measurement]
self.max = measurement
}
}
- actorinstance에 접근할 경우 await 키워드 사용
- await키워드를 사용하지 않을 경우 compile error 발생
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
* 참고
https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html