【问题标题】:Conforming an actor to Sequence protocol使参与者符合序列协议
【发布时间】:2021-10-14 04:43:42
【问题描述】:

如何使参与者符合Sequence 协议?以下代码生成编译器警告:

与全局actor 'MainActor' 隔离的实例方法'makeIterator()' 不能满足协议'Sequence' 的相应要求

@MainActor class Test: Sequence {
    private var contents: [Int] = []
    
    func makeIterator() -> Array<Int>.Iterator {
        contents.makeIterator()
    }
}

【问题讨论】:

  • 我不相信Actor可以符合Sequence。它的值不能在没有等待的情况下访问,这是 Sequence 所要求的。您应该能够使其符合 AsyncSequence。
  • 我的实际目标是符合RandomAccessCollection。我曾希望从符合Sequence 开始,这将是帮助我学习的一个很好的步骤,但我意识到 Swift 并发在多大程度上颠覆了我的理解。
  • 同样的事情; Actor 不能符合 RandomAccessCollection。这将特别成问题,因为count 和元素可能会在调用者的背后发生变化。 (如果内容是不可变的,那么它是一个 Actor 是没有意义的。)相反,Actor 应该将其内容作为属性/方法提供。例如,let elements = await actor.elements。那么elements 将是某个时间点 Actor 内容的快照,它肯定符合 RandomAccessCollection。
  • @RobNapier 只要将所需的方法标记为nonisolated,参与者仍然可以符合Sequence(或任何非异步协议,例如Hashable)。当然,您将无法访问任何与参与者隔离的属性,这意味着参与者可能不适合 SequenceAsyncSequence 更适合演员

标签: swift concurrency swift-concurrency


【解决方案1】:

将全局角色(如 @MainActor)应用于类型声明会在该类型的属性和方法中添加隐式 @MainActor

这意味着您的示例等效于:

class Test: Sequence {
    @MainActor private var contents: [Int] = []
    
    @MainActor 
    func makeIterator() -> Array<Int>.Iterator {
        contents.makeIterator()
    }
}

这是一个问题,因为Sequence 有一个makeIterator 要求,该要求并未与特定的全局参与者隔离。如果是这种情况,您将无法在后台线程中的 Array 上使用 for 循环。因此,要退出隐式隔离,您可以编写:

@MainActor class Test: Sequence {
    private var contents: [Int] = []
    
    nonisolated func makeIterator() -> Array<Int>.Iterator {
        // You can't use the isolated property 'contents'
        // here, because it's isolated
    }
}

也许您打算使Test 符合Sequence 类协议,其要求与@MainActor 隔离。不幸的是,类似的功能只是proposed 作为未来的方向。

如果您仍然感到困惑,那么值得退后一步,想想您真正想要实现的目标。 Actor 隔离提供了一种安全地改变跨线程共享的数据的方法。因此,没有隔离意味着缺乏突变(因此能够标记let 属性nonisolated)或除了actors 之外的同步机制。

【讨论】:

    猜你喜欢
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 2017-06-22
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多