-
Notifications
You must be signed in to change notification settings - Fork 0
TaskGroup 활용과 Task-Local 변수 #9
Copy link
Copy link
Open
Labels
Description
TaskGroup 활용
동시 실행 개수 제한
- 큰 배열에 대해 동시에 돌릴 작업 수를 제한
withThrowingTaskGroup+group.next()를 이용해 n개 실행 유지
func fetchValues(_ nums: [Int], limit: Int = 2) async throws -> [Int] {
// 자식 태스크가 Int를 반환하는 그룹 생성
try await withThrowingTaskGroup(of: Int.self) { group in
var it = nums.makeIterator()
var results: [Int] = []
// 최대 limit 만큼 먼저 addTask
for _ in 0..<limit {
if let n = it.next() {
group.addTask { n * 2 } // ex. 비동기 처리
}
}
// 완료된 작업 수집 + 다음 투입
while let value = try await group.next() {
results.append(value)
if let n = it.next() {
group.addTask { n * 2 }
}
}
return results
}
}실행
- 항상 최대 limit개의 자식 태스크가 돌아가도록 유지
- 끝나는 즉시
next()로 완료된 작업을 먼저 수집하고, 다음 작업을 투입 group.next()는 완료된 작업을 하나씩 반환(완료 순서대로)
입력: [1, 2, 3, 4], 작업: n * 2, limit = 2
시작
실행 중: {1}, {2}
results = []
1번 완료
실행 중: {2}, {3}
results = [2]
2번 완료
실행 중: {3}, {4}
results = [2, 4]
3번 완료
실행 중: {4}
results = [2, 4, 6]
4번 완료
실행 중: (없음)
results = [2, 4, 6, 8]
취소/에러 발생 시 중간 결과 반환
- 하나라도 실패하면 나머지 작업은 자동 취소 전파
- catch에서 지금까지 모은 결과를 돌려줄 수 있음
func fetchValuesPartial(_ nums: [Int]) async -> [Int] {
var partial: [Int] = []
do {
return try await withThrowingTaskGroup(of: Int.self) { group in
for n in nums {
group.addTask {
if n == 3 { throw NSError(domain: "", code: -1) }
return n * 2
}
}
// 완료 순서대로 방출
for try await v in group {
partial.append(v)
}
return partial
}
} catch {
return partial // 지금까지 수집한 값 반환
}
}Task-Local 변수
선언
@TaskLocal은 특정 Task 범위 내에서만 읽고 바인딩 가능한 타입 속성- 부모 Task에서 바인딩하면 자식 Task에도 값이 상속
enum DownloadOption: String {
case basic
case special
}
class Order {
@TaskLocal static var option: DownloadOption = .basic
func performTask() async {
print("\(Order.option.rawValue) 다운로드 시작")
if Order.option == .special {
print("추가적인 작업: \(Order.option.rawValue)")
}
}
}값 바인딩 (withValue)
withValue로 해당 블록 범위에서만 설정 -> 전역 상태 없이 컨텍스트를 전달- 블록이 끝나면 원래 값으로 복구
let order = Order()
Task {
await Order.$option.withValue(.basic) {
print("1번 화면 버튼 클릭")
await order.performTask()
await order.performTask()
}
// 블록 종료 후 기본값으로 복구 확인
print("블록 종료 후 현재 옵션:", Order.option)
}
Task {
await Order.$option.withValue(.special) {
print("2번 화면 버튼 클릭")
await order.performTask()
await order.performTask()
}
print("블록 종료 후 현재 옵션:", Order.option)
}실행
=== 1번 화면 버튼 클릭 ===
basic 다운로드 시작
basic 다운로드 시작
블록 종료 후 현재 옵션: basic
=== 2번 화면 버튼 클릭 ===
special 다운로드 시작
추가적인 작업 실행: special
special 다운로드 시작
추가적인 작업 실행: special
블록 종료 후 현재 옵션: basic
Reactions are currently unavailable