【问题标题】:Accessing actor properties synchronously from task-less context从无任务上下文同步访问参与者属性
【发布时间】:2022-01-16 18:31:33
【问题描述】:

actors 与现有代码集成似乎并不像Apple 希望您相信的那么简单。考虑以下简单的演员:

actor Foo {
    var value: Int = 0
}

试图从任何AppKit/UIKit(无任务)控制器访问此属性是行不通的,因为每个Task 都是异步的。

class AppKitController {

    func myTaskLessFunc() {
        let f = Foo()
        var v: Int = -1
        Task { v = await f.value }
    }
}

这会给你预期的错误Mutation of captured var 'v' in concurrently-executing code,如果你试图锁定它也不会起作用。 nonisolated actor 方法也无济于事,因为您会遇到同样的问题。

那么,如何在无任务上下文中同步读取 actor 属性呢?

【问题讨论】:

  • 这就是actor的作用——防止你在执行某些actor方法期间访问属性等。想象一下,如果有一个事务,其中一个actor方法将10添加到value,然后再次减去10。如果您同步访问它,您可能value10 时访问它。所以你应该异步阅读它。在您的 myTaskLessFunc 示例中,v 不会设置为函数完成执行的时间。
  • 我知道演员是如何工作的,但除非有办法从非异步上下文访问属性,否则有什么意义?在某些时候,您必须将结果转移到演员之外。那么当我不得不回退到类/锁定时,为什么还要使用演员呢?
  • 你可以试试nonisolatedreference
  • 就像我写的那样,nonisolated 也完全没有意义,因为它只是将问题转移到了不同​​的位置。我也无法访问 nonisolated func 中的 actor 属性。
  • 对不起,错过了那部分。对,那是真的。在这种情况下,是什么限制了 myTaskLessFunc 成为 async

标签: swift swift-concurrency


【解决方案1】:

我找到了使用Combine 的方法,如下所示:

import Combine

actor Foo {

    nonisolated let valuePublisher = CurrentValueSubject<Int, Never>(0)

    var value: Int = 0 {
        didSet {
            valuePublisher.value = value
        }
    }
}

通过提供非隔离发布者,我们可以安全地将actor值传播到外部,因为Publishers 是线程安全的。

调用者可以访问foo.valuePublisher.value 或从外部订阅它,而无需异步上下文。

【讨论】:

    猜你喜欢
    • 2022-08-20
    • 2021-08-29
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多