2-24.【Concurrency】AsyncStream 如何实现 producer/consumer 模型,保证线程安全?

0 阅读2分钟

1️⃣ 核心结论

AsyncStream 通过内部维护一个线程安全的队列和 continuation(挂起点)机制,实现 producer/consumer 模型。生产者可以在任意线程产生元素,消费者通过 for await 异步迭代获取元素,Swift runtime 自动保证线程安全和 Task 挂起/恢复。

一句话:内部队列 + continuation + executor 串行化 = 安全的异步流


2️⃣ AsyncStream 构成要素

  1. Producer

    • 调用 yield(_:) 产生元素
    • 可以在任意线程执行
  2. Consumer

    • for await element in stream 迭代元素
    • 消费者 Task 会在元素尚不可用时挂起
  3. Continuation

    • runtime 保存 Task 的挂起状态
    • 数据到达后 resume Task
  4. 内部队列

    • 缓存还未被消费的元素
    • 保证生产者和消费者不同线程也能安全交互

3️⃣ Producer/Consumer 流程

let stream = AsyncStream<Int> { continuation in
    // Producer
    Task.detached {
        for i in 0..<3 {
            continuation.yield(i)        // 产生元素
            try await Task.sleep(nanoseconds: 500_000_000)
        }
        continuation.finish()            // 流结束
    }
}

// Consumer
Task {
    for await element in stream {
        print(element)                  // 消费元素
    }
}

流程解释

  1. Producer 调用 yield

    • 将元素放入内部线程安全队列
    • 若有挂起的消费者 Task → 调用 continuation.resume() 恢复 Task
  2. Consumer 调用 next()/for await

    • 如果队列为空 → Task 挂起,状态保存在 continuation
    • 队列有数据 → 立即返回元素
    • Task resume 后继续循环
  3. 线程安全

    • yield 和 next() 可以在不同线程调用
    • 内部队列 + continuation + Swift runtime 保证同步访问
    • 无需显式锁

4️⃣ 内部原理

Producer Thread
   └─ yield(x)
       ├─ 入队 (Thread-safe)
       └─ 如果有挂起消费者 → resume Task

Consumer Task
   └─ await next()
       ├─ 队列有元素 → 返回
       └─ 队列空 → Task suspend → 保存 continuation

元素生成 → resume 挂起 TaskTask 获取元素
  • 队列:缓存未消费数据
  • continuation:挂起点,保存 Task 状态
  • resume:恢复挂起 Task
  • 串行化:每个元素消费顺序一致,线程安全

5️⃣ AsyncThrowingStream 也是类似机制

  • 区别在于 finish 可以带 error
  • Consumer 使用 for try await
  • 错误通过 continuation 恢复挂起 Task 并抛出

6️⃣ 优势总结

  1. 天然 producer/consumer

    • 生产者异步生成数据
    • 消费者异步迭代
  2. 线程安全

    • 内部队列 + continuation + runtime 串行化
    • yield/next 可跨线程调用
  3. 异步挂起/恢复

    • 避免阻塞线程
    • 支持结构化并发
  4. 可组合

    • 可以与 TaskGroup / async let 配合,构建复杂异步流水线

7️⃣ 面试必背总结

AsyncStream 通过内部线程安全队列 + continuation 实现 producer/consumer 模型。生产者可以在任意线程调用 yield 产生元素,消费者通过 for await 异步迭代获取元素;如果队列为空,消费者 Task 挂起,元素到达后由 runtime resume Task,从而保证线程安全和异步流顺序一致性。