iOS 개발 노트
🪴Async/Await에 대해서 본문
Async/Await에 대해서
Async/Await은 비동기 프로그래밍을 위한 새로운 패러다임이다.async
키워드를 사용하여 비동기 함수를 선언하고, await
키워드를 사용하여 해당 함수의 결과가 준비될 때까지 기다리는데, 이 과정에서 현재의 실행 흐름을 차단하지 않고 다른 작업을 계속 진행할 수 있다.
async
함수는 비동기적으로 실행되며, 결과를 반환하기 전에 완료될 필요가 있는 다른 비동기 작업을await
할 수 있다.- 비동기 함수 수행 중 문제가 발생하였을 때 error를 바로 리턴할 수 있도록 해준다
코루틴(Coroutine)
Coroutine은 함수가 동작하는 도중 특정 시점에 suspend(일시정지)할 수 있고, resume(다시 재개)할 수 있게 하는 비동기 함수 매커니즘이다.
- await 키워드가 있는 곳이 일시정지 지점을 나타냄.
- await 뒤에 있는 async 비동기 함수에 대해서 결과가 반환될 때까지 suspend가 작동된다.
- 결과가 반환되면 resume!
💡 suspend → resume의 과정
- await 키워드를 만나면 suspension point로 지정하고 일시정지 (suspend)
- 스레드의 제어권을 시스템에게 넘겨줌
- 비동기 함수의 작업 실행이 완료되었을 때, 시스템이 다시 비동기 함수에게 스레드 제어권을 넘겨줌
- suspension point에서 작업 재개 (resume)
Async/Await 장점
- 코드의 가독성 향상: 비동기 코드를 동기 코드와 유사하게 작성할 수 있어, 로직의 흐름을 쉽게 이해할 수 있습니다.
- 콜백 지옥 해결: 중첩된 콜백 대신 선형적인 코드 흐름을 사용하여, 복잡한 비동기 로직을 간결하게 표현할 수 있습니다.
- 에러 처리 용이:
try/catch
구문을 사용하여 비동기 작업에서 발생하는 에러를 효과적으로 처리할 수 있습니다. - self 참조 사이클이 생길 우려 제거
Async/Await 단점
- 성능 오버헤드: 비동기 작업이 많은 경우,
Async/Await
이 성능 면에서 약간의 오버헤드를 초래할 수 있습니다. 이는 작업을 스케줄링하고, 코루틴을 관리하는 데 필요한 추가적인 런타임 비용 때문일 수 있습니다. - 동기화 오버헤드: 동기화된 작업이 필요한 경우,
Async/Await
을 사용하면 추가적인 동기화 오버헤드가 발생할 수 있습니다. 이는 여러 비동기 작업 사이의 순서를 유지하거나 데이터를 공유할 때 발생할 수 있습니다.
Async/Await 사용해보기
함수명과 ->
기호 사이에 async
키워드를 써주면 해당 함수는 비동기로 실행된다.
func fetchData() async {
print("비동기로 실행됩니다.")
}
func fetchData() async -> Data {
print("비동기로 실행됩니다.")
}
async로 선언된 함수는 다른 async로 선언된 함수 내부 혹은 Task 클로저 내부(asynchronous context)에서만 호출할 수 있다. 그리고 async로 선언된 함수를 호출하기 위해서는 await 키워드와 함께 호출한다.
- Task 클로저 내부에 비동기로 수행할 작업을 넣어주면
asynchronous context
가 생성됨 - Task 클로저 내부에 작업을 배치하는 것은 DispatchQueue.global.async에서 호출하는 것과 유사함
- Task 클로저 내부에 있는 코드들은 처음부터 끝까지 순차적으로 실행됨
- await 키워드가 붙은 곳은 비동기 작업이 완료될 때까지 일시정지 상태가 됨
func fetchData() async -> String {
return "비동기로 실행됩니다."
}
func otherFunction() async {
let string = await fetchData()
print(string)
}
Task {
await otherFunction() //비동기로 실행됩니다.
}
실행 중 문제가 발생할 가능성이 있어 Error를 던지고 싶다면 async 키워드 뒤에 throws
키워드를 써주면 된다.
func fetchData() async throws -> Data
호출할 때는 await
앞에 try
키워드를 사용해준다.
Task {
do {
let data = try await fetchData()
// 데이터 처리 로직
} catch {
// 에러 처리 로직
}
}
GCD와 다르게 비동기 작업 결과 값을 탈출 클로저를 이용해 전달할 필요가 없어서 코드 가독성이 좋다.
func addNumWithAsync() async -> Int {
var result: Int = 0
for i in 1...5 {
result += i
}
return result
}
func callAsyncFunction() async {
let result = await addNumWithAsync() //비동기 작업이 완료된 시점에 result로 값을 전달함
print(result)
}
Task {
await callAsyncFunction() //15
}
각 독립적으로 동작하고 있는 Task들이 서로의 데이터를 공유하는 방법
Task {
let pineapplePicking = Task {
let pineapples = await harvestPineapples()
return pineapples.randomElement()!
}
let pineapple = await pineapplePicking.value
}
공유한 파인애플이 값타입이라면 복사해서 넘겨주면 되므로 아무런 문제가 없다. 하지만 파인애플이 참조 타입이라면, 2개의 Task에서 파인애플 클래스에 동시에 write를 할 가능성이 있는 Data Race 상황이 발생한다는 문제가 있다.
참조 타입처럼 데이터를 공유하는 방식이 필요할 경우 Actor
를 사용하면 된다.
Actor
한 번에 하나의 Task만 내부 상태를 조작하도록 허용함으로 동시 write로 인한 데이터 레이스를 피해주게 도와주는 것
- class, struct, enum과 같은 object type임
- 클래스와 마찬가지로 reference 타입임
- 클래스와 다르게 상속은 지원하지 않고, 암시적으로 Sendable임
- actor는 여러 스레드에서 동시에 실행 불가함
생긴 건 클래스와 비슷하고 참조 타입이다.
actor User {
var name: String = ""
var age: Int = 0
let gender: String = "male"
init(name: String, age: Int) {
self.name = name
self.age = age
}
func changeName(newName: String) {
self.name = newName
}
func changeAge(newAge: Int) {
self.age = newAge
}
}
하지만 외부 사용법에 있어서는 클래스와 차이가 있다.
- 상수는 바로 접근 가능하지만, 변수는 접근 시 await 키워드가 필요
- actor 내부 변수값을 외부에서 직접 수정하는 것은 불가능함. 프로퍼티 변경은 actor 내부에서만 가능
- actor 내부 메서드 호출 시 await 키워드 필요
Task {
let user = User(name: "daeyun", age: 28)
let gender = user.gender // 1. 상수는 변경 불가능하기 때문에 어느 스레드에서 접근해도 안전함. actor 외부에서도 바로 접근 가능
let age = await user.age // 2. actor 외부에서 변수 접근시 await 필요
await user.changeAge(newAge: 33) // 3. actor 외부에서 메서드 호출시 await 필요
await user.age += 100 // 4. ❌ 컴파일 에러. actor 외부에서 actor 내부의 변수를 변경할 수 없음
}
💡 다른 Task 들이 actor에 접근해서 코드 실행하고 있으면 해당 actor에 대한 다른 코드는 실행되지 못하고 기다려야 한다. 그래서 await 키워드를 사용하는 것이다. await을 이용해 여러 Task에서 동시에 실행될 수 없도록 방지하는 셈이다.
참고
https://tech.devsisters.com/posts/crunchy-concurrency-swift/
https://chatgpt.com/
https://sujinnaljin.medium.com/swift-actor-뿌시기-249aee2b732d
'Swift' 카테고리의 다른 글
🪴열거형(Enumeration) (0) | 2024.06.12 |
---|---|
🪴Swift는 Type-Safe한 언어! (0) | 2023.09.22 |
🪴변수명 표기법 (0) | 2023.06.29 |