一句话总览
Task {}= 有父任务、会继承上下文的结构化并发
Task.detached {}= 没有父任务、完全独立的非结构化并发
1️⃣ 最重要的区别(先看这个)
| 维度 | Task {} | Task.detached {} |
|---|---|---|
| 是否有父 Task | ✅ 有 | ❌ 没有 |
| 继承取消状态 | ✅ | ❌ |
| 继承优先级 | ✅ | ❌ |
| 继承 Task-local | ✅ | ❌ |
| 继承 actor / executor | ✅ | ❌ |
| 结构化并发 | ✅ | ❌ |
| 推荐使用 | ⭐⭐⭐⭐⭐ | ⭐⭐(谨慎) |
99% 的情况下,你应该用
Task {}
2️⃣ Task {} 到底是什么?
定义
Task {
await doSomething()
}
这是一个 child task(子任务) 。
它会继承什么?
假设你在这里:
@MainActor
func onTap() {
Task {
await loadData()
}
}
这个 Task 会继承:
- 当前 Task 的取消状态
- 当前 优先级
- 当前 Task-local 值
- 当前 actor(MainActor)
所以:
loadData() // 默认仍在 MainActor
除非你 await 到别的 executor。
为什么这很重要?
因为它保证了:
- 生命周期清晰
- 取消可以向下传播
- 行为可预测
👉 这就是“结构化并发”
3️⃣ Task.detached {} 是什么?
定义
Task.detached {
await doSomething()
}
这是一个 完全独立的任务。
特点(非常关键):
- ❌ 不属于当前 Task
- ❌ 不继承 MainActor
- ❌ 不继承优先级
- ❌ 不继承取消
- ❌ 不继承 Task-local
相当于:
“在并发世界里新开了一个孤儿线程(逻辑上)”
4️⃣ 一个最容易踩坑的例子 ⚠️
@MainActor
func onTap() {
Task.detached {
updateUI() // ❌ 运行期错误
}
}
原因:
- detached task 不在 MainActor
- 却访问了 MainActor 隔离的状态
必须显式切回:
Task.detached {
await MainActor.run {
updateUI()
}
}
5️⃣ 取消(cancellation)语义差异
Task {}
let task = Task {
try await longWork()
}
task.cancel()
- 子任务会被标记为 cancelled
await点会抛CancellationError- 和父任务强关联
Task.detached {}
let task = Task.detached {
try await longWork()
}
- 不会被父任务取消
- 必须手动管理生命周期
- 很容易“偷偷活很久”
6️⃣ 什么时候该用 Task {}?(几乎所有时候)
✅ UI 事件响应
Button("Load") {
Task {
await viewModel.load()
}
}
✅ actor 内部并发工作
actor Cache {
func refresh() {
Task {
await self.loadFromDisk()
}
}
}
✅ 需要自动取消、优先级继承
7️⃣ 什么时候才该用 Task.detached {}?
⚠️ 使用条件(缺一不可)
你真的需要:
- 不继承任何上下文
- 生命周期独立于调用者
- 手动处理取消 / actor hop
合理场景 1️⃣:全局后台维护任务
Task.detached(priority: .background) {
await cleanTempFiles()
}
- 不该被 UI 取消
- 明确是系统级任务
合理场景 2️⃣:跨 actor / 跨 subsystem 的桥接
Task.detached {
let data = await fetch()
await logger.log(data)
}
合理场景 3️⃣:底层库内部(而不是 App 代码)
Swift 官方建议:
Task.detached主要用于框架 / runtime 层
8️⃣ 官方态度(很明确)
Swift Concurrency 的设计哲学是:
默认结构化并发,尽量消灭 detached task
你可以理解成:
Task {}= safe defaultTask.detached {}= unsafe escape hatch
9️⃣ 快速记忆口诀 🧠
要继承上下文,用
Task
要彻底断亲,用Task.detached
不确定?别 detached
10️⃣ 最后一段总结(面试版)
Task {}创建的是结构化的子任务,会继承当前任务的取消、优先级、actor 和 task-local,适合绝大多数并发场景;
Task.detached {}创建的是非结构化任务,不继承任何上下文,生命周期完全独立,适合极少数系统级或框架级后台工作,普通业务代码应尽量避免。