一句话总结
Task cancellation 是结构化并发的天然伴生机制:父任务取消时,其子任务会自动收到取消信号,保证所有任务在作用域结束前要么完成,要么协作退出,从而避免资源泄露和孤儿任务。
1️⃣ 结构化并发与取消的天然联系
结构化并发的原则
-
任务树
- 父任务创建子任务 → 形成父子关系
-
生命周期绑定
- 子任务必须在父任务作用域结束前完成
-
错误和取消传播
- 父任务失败/取消 → 子任务收到通知
-
资源安全
- 子任务不会在父任务结束后“漂浮”
取消是父子传播的机制
-
父任务调用
cancel():- runtime 会遍历父任务的 子任务列表
- 给每个子任务设置
Task.isCancelled = true
-
子任务可以在 await 或 checkCancellation() 时响应取消
-
所以父任务作用域结束 = 子任务完成或被取消
2️⃣ async/await 下的取消行为
let parent = Task {
async let a = work1()
async let b = work2()
try await a + b
}
// 取消父任务
parent.cancel()
- async let 的子任务 a、b 自动收到取消
- 子任务在
await或内部检查取消时退出 - 父任务等待子任务结束 → 作用域安全
TaskGroup 的取消语义
try await withThrowingTaskGroup(of: Int.self) { group in
for id in ids {
group.addTask {
try await work(id)
}
}
for try await value in group { ... } // 自动收集
}
-
父 Task 被取消或任一子任务 throw:
- 所有子任务自动被标记为 cancelled
-
作用域结束前:
- runtime 等待所有子任务完成或退出
-
资源不会泄露
3️⃣ Cancellation 的协作式特性
- 不是强制 kill
- 子任务必须 显式检查或 await 才能停止
Task.checkCancellation()可以手动触发await会自动触发 cancellation 检查
group.addTask {
for i in 0..<1000 {
try Task.checkCancellation()
await doWork(i)
}
}
4️⃣ 父子关系保证取消正确性
- 父任务 cancel → 子任务收到信号
- 子任务 exit → 父任务等待(join)
- 确保没有孤儿任务
- 保证 Task-local、actor 状态正确释放
5️⃣ 总结关系图
Parent Task
├─ Child Task 1 ← cancelleable
├─ Child Task 2 ← cancelleable
└─ Child Task 3 ← cancelleable
Parent.cancel()
→ propagates to all children
→ children check cancellation in await / Task.checkCancellation()
→ children exit
→ parent waits for all children
6️⃣ 面试 / 核心记忆点
-
Task cancellation 是结构化并发的一部分
- 父子任务形成树,取消向下传播
-
子任务不会逃逸父任务作用域
- 自动 join → 避免孤儿任务和资源泄露
-
协作式取消
- await/checkCancellation → 响应取消
-
与 TaskGroup / async let / Task { } 一致
- 生命周期 + 错误 + 取消统一管理