一句话定义(先抓住本质)
Structured Concurrency(结构化并发) 是一种并发模型:
👉 并发任务的生命周期、取消、错误传播,被强制嵌入到程序的语法结构中。
更直白一点:
并发 ≠ 到处开任务,而是“有结构、有边界、有父子关系”。
1️⃣ 什么叫“结构化”?
来看这个对比。
非结构化并发(传统)
threadPool.submit {
doWork()
}
-
任务:
- 被丢进某个池子
- 跑多久、谁负责、什么时候结束 —— 不清楚
-
生命周期:
- 与调用点无关
-
取消:
- 基本没有统一模型
结构化并发
async func load() async {
async let a = fetchA()
async let b = fetchB()
return await a + b
}
-
子任务:
- 必须在函数返回前完成
-
生命周期:
- 像局部变量一样受作用域约束
-
并发是“嵌套的”
2️⃣ Structured Concurrency 的四大核心原则
① 生命周期有明确边界(最重要)
任务不能“逃出”创建它的作用域
func f() async {
Task { await work() } // structured
}
// work 在 f 返回前必须完成
没有“幽灵任务”。
② 父子关系(Task Tree)
Parent Task
├─ Child Task 1
├─ Child Task 2
└─ Child Task 3
-
父 task:
- 负责等待
- 负责取消
- 负责错误处理
③ 自动取消传播
parent.cancel()
→ 所有子任务自动取消。
④ 错误有结构化边界
-
一个子任务失败:
- 要么整体失败
- 要么被显式处理
没有“悄悄吞错”。
3️⃣ Structured Concurrency 在 Swift 里的体现
| 语法 | 是否结构化 |
|---|---|
async let | ✅ |
withTaskGroup | ✅ |
Task {} | ✅(子任务) |
Task.detached {} | ❌ |
4️⃣ 传统线程池模型是什么样的?
典型模型(Java / C++ / GCD)
executor.submit(() -> {
doWork();
});
核心特征:
-
线程 = 执行单位
-
任务是:
- 扔进去就不管了
-
生命周期:
- 与调用点解耦
-
错误:
- 常常丢失
-
取消:
- 很弱或没有
5️⃣ 核心区别:不是“有没有线程池”
很多人误以为:
structured concurrency = 没有线程池
这是错的。
👉 Swift 底层依然是线程池 + work stealing。
真正的区别在于:
| 维度 | Structured Concurrency | 线程池模型 |
|---|---|---|
| 抽象层级 | Task | Thread |
| 生命周期 | 作用域约束 | 全局 |
| 父子关系 | 显式 | 隐式 / 无 |
| 取消 | 自动传播 | 手动 |
| 错误 | 结构化传播 | 易丢失 |
| 可推理性 | 高 | 低 |
6️⃣ 一个非常直观的对比示例
线程池式代码(非结构化)
func load() {
executor.submit {
let a = fetchA()
executor.submit {
let b = fetchB()
print(a + b)
}
}
}
你几乎无法推理:
- 哪个先跑?
- 哪个失败了?
- 谁负责回收?
Structured Concurrency
func load() async -> Int {
async let a = fetchA()
async let b = fetchB()
return await a + b
}
你一眼就知道:
- 并发发生在哪
- 在哪里 join
- 错误怎么传播
7️⃣ 为什么 Structured Concurrency 更安全?
🚫 消灭“共享可变状态”
-
子任务:
- 返回值
- 不写共享变量
-
父任务:
- 串行处理结果
🚫 消灭“悬挂任务”
-
编译器 + runtime 保证:
- 离开作用域前完成
🚫 消灭“忘记取消”
- 自动 cancel propagation
8️⃣ Swift 为什么强推 Structured Concurrency?
因为 Swift 的目标是:
让并发代码“像同步代码一样可读、可推理、可验证”。
这在:
- actor 隔离
- async/await
- TaskGroup
- Sendable
是一整套体系。
9️⃣ 一句话终极总结(面试必背)
Structured Concurrency 是一种并发模型,它强制并发任务的生命周期、取消和错误传播与程序的语法结构保持一致;
与传统线程池模型相比,它不以线程为中心,而以任务树为中心,从而显著提升了并发代码的可推理性和安全性。