You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何不能将Actor用作AsyncSequence中的状态机类型?

关于WWDC 2023《Beyond the basics of structured concurrency》中状态机为何不使用Actor的解惑

笔记内容

以下是WWDC 2023会话Beyond the basics of structured concurrency的笔记内容:

许多AsyncSequence是通过状态机实现的,我们用状态机来停止运行中的序列。

示例代码:

public func next() async -> Order? {
    return await withTaskCancellationHandler {
        let result = await kitchen.generateOrder()
        guard state.isRunning else {
            return nil
        }
        return result
    } onCancel: {
        state.cancel()
    }
}

笔记要点:

  • 尽管Actor非常适合保护封装状态,但它无法真正保护状态机。
  • 对于修改和读取状态机的单个属性,Actor并非合适的工具。
  • 无法保证Actor上操作的运行顺序,因此无法确保取消操作优先执行。
  • 建议改用Swift Atomics包中的原子操作、dispatch队列或锁。

推荐的实现方式:

private final class OrderState: Sendable {
    let protectedIsRunning = ManagedAtomic<Bool>(true)
    var isRunning: Bool {
        get { protectedIsRunning.load(ordering: .acquiring) }
        set { protectedIsRunning.store(newValue, ordering: .relaxed) }
    }
    func cancel() { isRunning = false }
}

核心疑问解答

为什么Actor在这里不是合适的选择?

Actor的核心逻辑是通过串行化消息队列保证状态安全,所有对Actor状态的访问、修改请求都要排队执行。但在这个AsyncSequence的取消场景里,我们的需求是取消操作能立即生效,且后续状态检查能立刻感知到取消结果,Actor的特性刚好和这个需求冲突:

  1. 操作优先级无法保障:当kitchen.generateOrder()正在执行时触发取消(调用state.cancel()),如果state是Actor,这个取消请求会被放到Actor的消息队列末尾。而generateOrder()完成后,读取state.isRunning的请求也会进入队列,极有可能出现「先执行状态读取、再执行取消操作」的顺序——这就导致我们无法及时检测到取消,依然会返回任务结果,完全违背了取消的意图。

  2. 不必要的调度开销:Actor的每一次状态访问都需要经过异步调度,对于isRunning这种简单布尔值的读写来说,这种开销是冗余的。原子操作、锁或者串行队列能以更低的成本完成状态同步,更适配这种轻量状态机的场景。

  3. 状态即时性需求不匹配:状态机的状态变化需要即时可见,Actor的异步特性会让状态的读取、修改存在延迟,无法满足状态机对状态一致性的即时性要求。而原子操作可以保证内存访问的顺序性和可见性,确保取消操作执行后,后续的状态读取能立刻获取到最新值。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 08:08:13