-
Notifications
You must be signed in to change notification settings - Fork 0
작업 취소 #8
Copy link
Copy link
Open
Labels
Description
작업 취소 (Task Cancellation)
-
즉시 강제 종료가 아닌, 취소 전파
-
취소되면
Task.isCancelled == true로, 실제 작업 중단은 코드에서 구현 필요 -
구조적 동시성에서는 부모 Task가 취소되면 자식(
async let,TaskGroup)에도 취소가 전파 -
한 번 취소된 Task는 되돌릴 수 없음
부모 Task (cancel)
├─ 자식 A ← 취소 전파
├─ 자식 B ← 취소 전파
└─ 자식 C ← 취소 전파
취소 확인
task.cancel()→ Task에 취소 신호 전송Task.isCancelled→ 현재 Task가 취소되었는지 확인
let task = Task {
while !Task.isCancelled {
// 반복해서 일 수행
try await Task.sleep(for: .milliseconds(50))
}
// 루프 빠져나오면(Task.isCancelled == true) 정리 후 종료
}
print(task.isCancelled) // false
task.cancel() // 신호 전파
print(task.isCancelled) // truetry Task.checkCancellation()→ 취소 시CancellationError던짐
try Task.checkCancellation() // 여기서 바로 throw → 아래 코드 실행 안 됨취소 핸들러
withTaskCancellationHandler(operation:, onCancel:)- 취소 시
onCancel클로저는 즉시 실행됨 - 내부 동작이 async가 아니어도 정리 작업 가능
let task = Task {
try await withTaskCancellationHandler(operation: {
// 본 작업
try await Task.sleep(for: .seconds(5))
}, onCancel: {
// 취소되면 즉시 실행
})
}
task.cancel() 기본적으로 취소를 지원하는 API
- Task가 취소 상태로 바뀌면 자동으로 에러(
CancellationError)를 던지고 종료 - 별도 체크 없이도 작업이 중단
URLSession
URLSession.shared.data(from:)내부는 취소를 지원task.cancel()이 호출되면, 중간에CancellationError를 던지고 네트워크 요청을 취소 → Task 종료
let task = Task {
let url = URL(string: "https://example.com")!
let (data, _) = try await URLSession.shared.data(from: url)
// 네트워크 응답
}
// 바로 취소
task.cancel()Task.sleep
- 취소되면 기다리던 중간에
CancellationError던지고 종료
let task = Task {
try await Task.sleep(for: .seconds(5))
}
Task {
try await Task.sleep(for: .seconds(2))
task.cancel() // 2초 후 취소
}구조적 동시성과 취소 전파
async let
- 부모 Task가 취소되면 자식도 취소됨
- 하나의 자식에서 에러 발생 → 다른 자식에게도 취소 전파
Task {
// a, b, c는 동시 실행 → 결과는 나중에 await 시점
async let a = fetchA() // async throws
async let b = fetchB() // async throws
async let c = fetchC() // async throws
do {
_ = try await (a, b, c)
} catch {
// 여기 들어오면 나머지 미완료 자식에 취소 전파
// ex. b가 네트워크 에러를 던지면, try await (a, b, c) 전체가 throw
// -> 나머지 실행 중인 자식(a, c)은 자동으로 취소 신호를 받음
}
}TaskGroup
- 부모 취소 -> 그룹 전체 자식 취소
- 에러 전파 시에도 나머지 작업들이 자동 취소
- 필요 시 명시적으로
group.cancelAll()가능
// `fetchAll`을 호출한 쪽에서 Task를 취소하면,
// `withThrowingTaskGroup` 안의 모든 자식 Task도 자동으로 취소 신호를 받음
func fetchAll(urls: [String]) async throws -> [UIImage] {
try await withThrowingTaskGroup(of: UIImage.self) { group in
for url in urls {
group.addTask {
try await fetchImage(url) // 자식 중 하나라도 throw → 나머지 자동 취소
}
}
var results: [UIImage] = []
for try await image in group {
results.append(image)
}
return results
}
}Reactions are currently unavailable