일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- ios
- coreml
- WWDC22
- app intents
- spring
- tabman
- Flutter
- swift concurrency
- SwiftUI
- 코테
- widgetkit
- UITableView
- backend
- createml
- Delegate Pattern
- Project
- Coding Test
- github
- Swift
- watchkit
- UIStackView
- UIDatePicker
- cloud functions
- Firebase
- task.yield()
- Complication
- Apple Developer Academy
- Tuist
- Sendbird
- fcm
- Today
- Total
azhy의 iOS 이야기
Task.yield()는 언제 쓸까? 본문
Suspension points
Swift Concurrency의 장점 중 하나로 소개된 것이 스레드를 코어 수만큼만 사용해서 콘텍스트 스위칭이 적다는 것이었습니다.
하지만 반대로 스레드 개수가 제한되어 있어서 스레드가 작업을 오래 들고 있으면 다른 작업이 밀리는 상황이 발생할 수도 있습니다.
이러한 문제점을 어떻게 해결할까요?
여기서 바로 Suspension points가 등장합니다.
Suspension points는 OS에서 Swift Concurrency 작업의 스레드 점유권을 가져와 다른 작업에 할당할 수 있는 지점입니다.
await 키워드를 잠재적 Suspension points라고도 합니다.
스레드가 작업을 오래 붙잡지 못하게 OS가 적절하게 스레드 점유권을 다른 작업에게 양도한다는 뜻이죠
await 키워드를 만난다 하더라도 무조건 스레드 점유권을 양도하는 게 아니라 다른 작업에 양도해야 하는 상황으로 판단하면 양도하게 됩니다.
하지만
Suspension points가 없는 async 함수, 예를 들면 await 키워드가 없는 async 함수가 될 수 있겠죠?
import Foundation
func processLargeDataset() async {
// 오래 걸리는 작업
}
이런 함수가 만약 스레드를 오래 붙잡는 작업을 하게 된다면 문제가 발생합니다.
Suspension points가 없기 때문에 스레드 점유권을 양도하지 않아 한 작업이 종료될 때까지 다른 작업은 실행되지 않습니다.
Swift Concurrency에서 스레드 수는 코어 수로 한정되어 있어서 이런 문제가 많이 발생하면 앱이 느려질 가능성이 있습니다.
이때 사용할 수 있는 것이 바로 Task.yield() 입니다.
Task.yield()
import Foundation
func processLargeDataset() async {
// 오래 걸리는 작업
await Task.yield()
}
Task.yield()를 호출하면 명시적으로 해당 부분에서 Suspension points를 만들어 스레드 점유권을 양도할 수 있게 합니다.
OS가 판단해서 다른 작업에 양도해야 하는 상황으로 판단하면 양도하게 되죠.
이러한 커스텀 async 함수를 작성할 때에는 Suspension points가 있는 코드인지를 잘 확인해야 한다는 것입니다. Suspension points가 없다면 작업은 자발적으로 스레드를 양도하지 않아서 Swift Concurrency의 한정된 스레드를 오래 점유할 수 있습니다.
해외 아티클에서 Task.yield()는 이럴 때 사용하라고 하네요.
When to Use Task.yield
- In long-running loops or tasks.
- During CPU-intensive operations.
- To maintain a balance between task progress and app responsiveness.
결론
커스텀 async 함수를 작성할 때에는 Suspension points가 있는지 잘 확인하며 작성해야 한다. Suspension points가 없다면 동시성을 활용하지 못할 수 있다. 그때는 적절하게 Task.yield()를 사용하자!
Task 블록 사용 시 주의할 점
아래와 같은 함수에서 someSubFunction이 async 함수로 변경됐다고 가정하겠습니다.
func someFunction() {
...
let someVariable = someSubFunction(...)
...
}
이상적인 대응 방법은 someFunction을 async 함수로 변경하고 someSubFunction을 사용하는 것이지만, 특정 경우에는 someFunction을 async 함수로 변경하지 못할 수도 있습니다.
보통 이런 경우에는 Task 블록을 이용해 await 키워드가 있는 부분을 묶어줍니다. 코드로 보면 아래와 같습니다.
func someFunction() {
...
Task {
let someVariable = await someSubFunction(...)
... // Code A
}
}
여기서 Task 블록 안과 밖은 다른 환경이므로 someVariable을 사용하는 코드인 code A는 Task 블록 안에 위치해야 하는데요. 이때 code A가 실행하는 데 오래 걸리는 코드라면 Swift Concurrency가 작동하는 스레드를 오래 점유할 수 있습니다.
만약 someSubFunction만 async 함수로 변경된 상황이라면 code A는 Suspension points를 갖고 있지 않을 텐데요. Suspension points 없이 Task 블록 안에서 오래 작동하면 앞서 말씀드렸듯 동시성을 살려 작동하지 않기 때문에 다른 작업의 시작을 늦출 수 있습니다.
coda A가 오래 걸리는 작업이라면 Task.yield()를 통해 Suspension points를 추가해 주면 좋을 거 같습니다.
'Swift' 카테고리의 다른 글
[iOS/Swift] iOS 15, Navigation Bar 배경 색 설정 (0) | 2024.11.14 |
---|---|
[iOS/Swift] lazy Variable (4) | 2024.11.13 |
[iOS/Swift] setContentHuggingPriority, setContentCompressionResistancePriority 개념 (0) | 2024.11.13 |
[iOS/Swift] UIDatePicker 최소, 최대 날짜 설정하기 (0) | 2024.11.12 |
[iOS/Swift] Lottie 를 사용해서 애니메이션을 만드는 법 (2) | 2024.11.12 |