一句话对照结论(先记住)
structured concurrency 的核心是“继承 + 约束”,
DetachedTask 的本质是“不继承 + 不约束”。
1️⃣ structured concurrency 到底在“继承”什么?
以一个普通的结构化子任务为例:
Task {
await work()
}
这是 structured concurrency,它会继承父任务的上下文:
✅ 继承内容(全部)
| 上下文 | 是否继承 | 意义 |
|---|---|---|
| 父 Task | ✅ | 生命周期绑定 |
| cancellation | ✅ | 父取消 → 子取消 |
| priority | ✅ | 调度一致 |
| Task-local | ✅ | 逻辑上下文传递 |
| actor / executor | ✅ | 隔离 & 顺序保证 |
| 错误边界 | ✅ | 统一失败语义 |
👉 这些继承 + 作用域约束,构成了 structured concurrency。
2️⃣ DetachedTask 的上下文继承规则
Task.detached {
await work()
}
DetachedTask 的规则是 显式断开一切上下文:
❌ 不继承任何父上下文
| 上下文 | 继承? |
|---|---|
| 父 Task | ❌ |
| cancellation | ❌ |
| priority | ❌ |
| Task-local | ❌ |
| actor / executor | ❌ |
| 错误边界 | ❌ |
DetachedTask 只继承:
- 全局默认 executor
- 明确指定的 priority(如果你传了)
3️⃣ “继承 vs 不继承”带来的根本差异
① 生命周期(最关键)
structured concurrency
Task {
Task {
await work()
}
}
// 父 task 结束前,子 task 必须结束
- 子任务不能逃逸
- 不会有“幽灵任务”
DetachedTask
Task {
Task.detached {
await work()
}
}
// 父 task 结束了,detached 还在跑
-
detached 任务:
- 可以比父任务活得更久
- 完全脱离调用栈
② cancellation 语义
-
structured:
- 取消是 树状传播
-
detached:
- 取消是 完全手动
let t = Task.detached { ... }
t.cancel() // 必须自己做
③ actor 隔离 & 执行上下文
这是 最容易踩坑的地方。
@MainActor
func tap() {
Task {
updateUI() // OK
}
Task.detached {
updateUI() // 💥 runtime error
}
}
原因:
- detached task 不在 MainActor
- structured task 会自动继承 MainActor
④ 可推理性(设计层面)
structured concurrency:
-
你可以 仅看代码结构
-
推理:
- 任务何时开始
- 何时结束
- 谁负责取消
DetachedTask:
-
必须:
- 全局搜索
- 手动跟踪
-
非局部推理
4️⃣ 为什么 Swift 要“刻意”提供 DetachedTask?
既然这么危险,为什么还要它?
官方设计理由只有一个:
作为“逃生舱(escape hatch)”存在
合理使用场景(非常少)
✅ 1️⃣ 系统级后台维护
Task.detached(priority: .background) {
await cleanCache()
}
- 不应被 UI 生命周期影响
✅ 2️⃣ 框架 / runtime 内部
-
你在写:
- logging
- metrics
- telemetry
-
不知道调用者是谁
✅ 3️⃣ 跨 subsystem 的桥接
-
明确知道:
- 不该继承 actor
- 不该继承取消
5️⃣ Swift 的设计立场(非常明确)
Swift Concurrency 的核心价值是:
默认结构化,显式打破结构
这也是为什么:
Task {}是短名字Task.detached {}是长名字 + 明确警告语义
6️⃣ 一个非常形象的类比 🧠
-
structured concurrency:
- 像 函数调用栈
- 生命周期清晰
-
DetachedTask:
- 像 手动 malloc
- 你得自己 free(取消)
7️⃣ 面试级总结(背这个)
structured concurrency 中的任务会继承父任务的上下文(取消、优先级、Task-local、actor),并受作用域约束,生命周期可推理;
DetachedTask 刻意不继承任何上下文,生命周期完全独立,是一种非结构化并发,仅应在少数系统级或框架级场景作为逃生舱使用。