深入理解 Swift Concurrency:从 async/await 到 Actor 与线程池的完整运行机制

一、async 函数的本质:可挂起的函数

✅ 什么是 async 函数?

swift 复制代码
func asyncWork() async {
    // 这是一个异步函数
}
  • async 函数是可以被挂起的函数。
  • 挂起 ≠ 阻塞线程,而是让出线程去执行其他任务。
  • 挂起只发生在 await 处,称为潜在挂起点。

✅ sync 函数可以当作 async 函数使用,反之不行

swift 复制代码
protocol SomeProtocol {
    func work() async
}

struct SomeStruct: SomeProtocol {
    func work() {
        // ✅ 合法:sync 实现 async 协议方法
    }
}
swift 复制代码
protocol SomeProtocol {
    func work()
}

struct SomeStruct: SomeProtocol {
    func work() async {
        // ❌ 非法:async 实现 sync 协议方法
    }
}

二、await:异步等待,非阻塞线程

✅ 示例:顺序执行异步代码

swift 复制代码
func asyncWork() async -> Int {
    return 42
}

let result = await asyncWork() // 不会阻塞线程
  • await 是潜在挂起点,只有当调用者和被调用者执行上下文不同时,才会真正挂起。
  • 挂起时,系统会保存当前状态(称为 continuation),并释放线程。

三、Continuation:挂起点的"快照"

  • 包含:返回地址、参数、局部变量等。
  • 存储在堆中,允许跨线程恢复。
  • 由运行时管理,开发者无需直接操作。

四、Task:异步执行的单位

✅ 创建 Task 进入异步上下文

swift 复制代码
Task {
    await callAsyncFunc()
}
  • 每个 async 函数都在某个 Task 中运行。
  • Task 是异步函数的"容器",类似线程之于 sync 函数。
  • Task 本身不具备并发能力,一个 Task 一次只执行一个函数。

✅ Task 的三种状态

  • 🔴 suspended:等待调度或外部事件
  • 🟡 running:正在线程上运行
  • 🟢 completed:执行完成

五、Job:Task 的"同步片段"

swift 复制代码
Task {
    beforeWork()
    await asyncWork()
    afterWork()
}
  • 每个 await 将 Task 拆分为多个 Job。
  • Job 是同步执行的最小单位,不包含 await
  • Job 按顺序执行,不能并发。

六、Actor:线程安全的并发模型

✅ 示例:Actor 隔离状态

swift 复制代码
actor SomeActor {
    let immutableState = 1
    var mutableState = 2

    func updateState(_ newValue: Int) {
        mutableState = newValue
    }
}

let actor = SomeActor()

print(actor.immutableState) // ✅ 无需 await

Task.detached {
    await print(actor.mutableState) // ✅ 需 await
    // actor.mutableState = 3 // ❌ 编译错误
    await actor.updateState(3) // ✅ 合法
}
  • Actor 保证同一时间只有一个任务访问其可变状态。
  • 不可变状态可同步访问,无需 await
  • 可变状态必须通过 await 调用 actor 方法访问。

七、Executor:Job 的执行器

  • Executor 负责将 Job 调度到线程执行。
  • 类型:
    • Default concurrent executor:非 actor 隔离任务
    • Serial executor:每个 actor 一个,顺序执行
    • Main executor:主线程,处理 @MainActor 任务

八、Cooperative Thread Pool(CTP):线程池

  • Swift Concurrency 使用固定数量线程(= CPU 核心数),避免线程爆炸。
  • 所有 executor(除主线程)都从 CTP 借线程。
  • 主线程独立,不参与 CTP。

九、线程与 executor 的映射关系

swift 复制代码
Task.detached {
    // 默认并发 executor,CTP 线程
    await someAsyncFunction()
    // 仍为默认并发 executor,可能换线程
}
swift 复制代码
actor SomeActor {
    func someMethod() async {
        // 当前 actor 的 serial executor
        await someAsyncFunction()
        // 仍为同一 actor executor,可能换线程
    }
}
swift 复制代码
Task { @MainActor in
    // 主线程
    await someAsyncFunction()
    // 仍在主线程
}

十、完整运行机制图解(文字版)

csharp 复制代码
CPU 核心
   ↓
Cooperative Thread Pool(固定线程)
   ↓
Executor(调度 Job)
   ↓
Job(同步片段)
   ↓
Task(异步函数容器)
   ↓
async/await(挂起点与 continuation)
   ↓
Actor(隔离状态,防止数据竞争)

十一、总结与个人见解

✅ Swift Concurrency 的优势

  • 结构化并发:Task、async/await、actor 构成完整模型。
  • 线程安全:Actor 提供编译期保证,避免数据竞争。
  • 性能优化:CTP 限制线程数,避免线程爆炸。
  • 可读性强:异步代码像同步代码一样线性书写。

⚠️ 学习曲线与挑战

  • 概念多,机制复杂,需理解底层模型。
  • 调试困难,尤其是线程切换与挂起恢复。
  • 与旧代码(GCD、delegate)集成需谨慎。

十二、扩展使用场景与实践建议

✅ 场景 1:网络请求 + 数据解析

swift 复制代码
func fetchData() async throws -> Data {
    let url = URL(string: "https://api.example.com")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

✅ 场景 2:并发请求多个接口

swift 复制代码
async let user = fetchUser()
async let posts = fetchPosts()
let result = await (user, posts)

✅ 场景 3:Actor 管理缓存

swift 复制代码
actor ImageCache {
    private var cache: [String: UIImage] = [:]

    func image(for key: String) -> UIImage? {
        return cache[key]
    }

    func save(image: UIImage, for key: String) {
        cache[key] = image
    }
}
相关推荐
HarderCoder2 小时前
【Swift Concurrency】深入理解 `async let` 与 `TaskGroup`:并发任务的生命周期与错误传播机制
swift
HarderCoder2 小时前
Swift 结构化并发 6 条铁律 —— 一张图 + 一套模板,让 `async let` / `TaskGroup` / `Task {}` 不再踩坑
swift
M-finder1 天前
Mac菜单栏综合工具FancyTool更新啦
mac·swift
HarderCoder3 天前
在同步代码里调用 async/await:Task 就是你的“任意门”
swift
HarderCoder3 天前
Swift 三目运算符指南:写法、场景与避坑
swift
YungFan3 天前
iOS26适配指南之UISlider
ios·swift
HarderCoder3 天前
一篇读懂 Swift 不透明类型:让带 associatedtype 的协议也能当返回值
swift
HarderCoder3 天前
`@dynamicCallable`:把 Swift 对象当函数喊
swift
HarderCoder4 天前
调试 Swift 并发:我到底在哪个 Actor?
swift