这是WWDC 2023年第Beyond the basics of structured concurrency届会议的笔记.

许多AsyncSequences是用状态机实现的,我们使用状态机来停止运行的序列.

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

  • 虽然参与者非常擅长保护封装状态,但他们并不能真正保护状态机.
  • 修改和读取状态机上的各个属性,参与者并不是完全正确的工具.
  • 无法保证操作在参与者上运行的顺序,因此我们无法确保我们的取消将首先运行.
  • 使用Swift Atomics包中的原子、调度队列或锁.
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 }
}

看完这部分后,我不明白为什么演员不是一个好的 Select .请对此进行一些解释.

推荐答案

您包含了解释为什么国家不能成为行动者的 comments 之一:

我们无法保证操作在参与者上运行的顺序,因此我们无法确保我们的取消将首先运行.

因此,参与者不太适合状态机的同步.当您取消序列时,您希望确保取消将首先运行.

对于那些不熟悉演员非先进先出行为的人,我会推荐您参阅SE-0306 – Actors,其中写道:

默认的序列执行者负责一次运行一个部分任务.这在概念上类似于连续剧DispatchQueue,但有一个重要的区别:等待演员的任务并不能保证按照最初等待该演员的顺序运行..这与严格先进先出的系列DispatchQueue形成鲜明对比.

此外,这video还解释了第二个问题(强调部分添加):

我们通过synchronously调用序列状态机上的cancel函数来实现这一点..这些机制[锁、对GCD序列队列的同步调用或原子]允许我们同步共享状态,避免竞争条件,同时允许我们取消正在运行的状态机,而无需在取消处理程序中引入非 struct 化任务.

他们建议状态对象的同步应该是同步的.他们建议不要启动非 struct 化任务来更新状态机(如果它是参与者,您就必须这样做).

简而言之,当您取消一个序列时,您确实希望确信不再产生更多元素,这意味着同步同步,即不是演员,而是像锁、原子或GCD序列队列这样的东西.


为了完整起见,以下是that video的完整引文:

与同步迭代器一样,next函数返回下一个 序列中的元素,或者nil表示我们处于 序列.许多AsyncSequences是通过状态实现的 机器,我们使用它来停止运行序列.

在我们这里的例子中,当isRunning为真时,序列应该 继续下达命令.一旦任务取消,我们需要 指示序列已完成并应关闭.

我们通过同步调用我们的cancel函数来实现这一点 序列状态机.

请注意,由于取消处理程序立即运行,因此状态 机器在取消处理程序和 主体,可以并发运行.我们需要保护我们的国家 机虽然演员非常擅长保护封装状态,但我们 想要修改和读取状态机上的各个属性,因此 演员并不是解决这个问题的合适工具.

此外,我们无法保证操作在 演员,所以我们无法确保我们的取消会首先运行.我们会 需要其他东西.我决定使用Swift Atomics中的原子 包,但我们可以使用调度队列或锁.

这些机制使我们能够同步共享状态,避免 竞赛条件,同时允许我们取消正在运行的状态机 无需在取消处理程序中引入非 struct 化任务.

Swift相关问答推荐

出错-无法将季节类型的值转换为预期的参数类型';[水果]';

SWIFT异步/等待,多个监听程序

如何在DATE()中进行比较

调度组是否阻止iOS中的主线程?

关闭 SwiftUI TabView 中的子视图

如何从 Swift 中隐藏 Objective-C 类接口的一部分?

从文字创建数组时的 Swift 宏

Swift // Sprite Kit:类别位掩码限制

如何在不将变量转换为字符串的情况下判断变量在 Swift 中是否为 Optional(nil)?

如何延迟 swift 属性 didSet 使其每秒只触发一次

如何为 Swift UI 视图定义 struct ?

P384 公钥获取IncorrectParameterSize

Swift:withCheckedContinuation 和 Dispatch QoSClass

为什么更改属性后动画会加速? SwiftUI

如何在 Swift 中返回 Task 中定义的变量

临时添加到视图层次 struct 后,Weak view引用不会被释放

在 Swift 中强制崩溃的最简单方法

调整文本的字体大小以适应 UIButton

Swift 中的 CommonHMAC

如何快速从另一个视图控制器重新加载表格视图