What is an actor and why does Swift have them? 什么是 actor,为什么 Swift 有它们?

What is an actor and why does Swift have them? 什么是 actor,为什么 Swift 有它们?

温馨提示:本文最后更新于2025-02-10 14:34:44,某些文章具有时效性,若有错误或已失效,请在下方留言

Swift’s actors are conceptually like classes that are safe to use in concurrent environments. This safety is made possible because Swift automatically ensures no two pieces of code attempt to access an actor’s data simultaneously – it is made impossible by the compiler, rather than requiring developers to write boilerplate code using systems such as locks.
Swift 的 actor 在概念上类似于在并发环境中安全使用的类。之所以能够实现这种安全性,是因为 Swift 会自动确保没有两段代码试图同时访问 actor 的数据 – 编译器使这成为不可能,而不是要求开发人员使用锁等系统编写样板代码。

In the following chapters we’re going to explore more about how actors work and when you should use them, but here is the least you need to know:
在接下来的章节中,我们将更多地探讨 actors 是如何工作的,以及何时应该使用它们,但以下是你至少需要了解的内容:

  1. Actors are created using the actor keyword. This is a concrete nominal type in Swift, like structs, classes, and enums.
    Actor 是使用 actor 关键字创建的。这是 Swift 中一个具体的名义类型,就像结构体、类和枚举一样。
  2. Like classes, actors are reference types. This makes them useful for sharing state in your program.
    与类一样,Actor 也是引用类型。这使得它们可用于在程序中共享状态。
  3. They have many of the same features as classes: you can give them properties, methods (async or otherwise), initializers, and subscripts, they can conform to protocols, and they can be generic.
    它们具有许多与类相同的功能:您可以为它们提供属性、方法(异步或其他)、初始化器和下标,它们可以符合协议,并且可以是泛型的。
  4. Actors do not support inheritance, so they cannot have convenience initializers, and do not support either final or override.
    Actor 不支持继承,因此它们不能具有方便的初始化器,并且不支持 final 或 override
  5. All actors automatically conform to the Actor protocol, which no other type can use. This allows you to write code restricted to work only with actors.
    所有 Actor 都自动符合 Actor 协议,其他类型无法使用该协议。这允许您编写仅限于 actor 的代码。

As well as those, there is one more behavior of actors that lies at the center of their existence: if you’re attempting to read a variable property or call a method on an actor, and you’re doing it from outside the actor itself, you must do so asynchronously using await.
除了这些之外,Actor 还有一个行为是其存在的中心:如果您尝试读取变量属性或调用 actor 上的方法,并且您是从 actor 本身外部执行的,则必须使用 await 异步执行此作。

I’ll explain why in a moment, but I want to show you a brief snippet of code first so you can see what I mean:
我稍后会解释原因,但我想先向您展示一段简短的代码,以便您明白我的意思:

actor User {
    var score = 10

    func printScore() {
        print("My score is \(score)")
    }

    func copyScore(from other: User) async {
        score = await other.score
    }
}

let actor1 = User()
let actor2 = User()

await print(actor1.score)
print(await actor1.score)
await actor1.copyScore(from: actor2)
下载图标
what-is-an-actor-and-why-does-swift-have-them-1.zip
zip文件
50.3K

You can see several things in action there:
您可以在那里看到几个实际作:

  1. The new User type is created using the actor keyword rather than struct or class.
    新的 User 类型是使用 actor 关键字而不是 struct 或 class 创建的。
  2. It can have properties and methods just like structs or classes.
    它可以像结构或类一样具有属性和方法。
  3. The printScore() method can access the local score property just fine, because it’s our actor’s method reading its own property.
    printScore() 方法可以很好地访问本地 score 属性,因为它是我们 actor 的方法读取自己的属性。
  4. But in copyScore(from:) we’re attempting to read the score from another user, and we can’t read their score property without marking the request with await.
    但是在 copyScore(from:) 中,我们尝试从另一个用户那里读取分数,如果不用 await 标记请求,我们无法读取他们的 score 属性。
  5. Code from outside the actor also needs to use await.
    来自 actor 外部的代码也需要使用 await
  6. We can use await print(actor1.score) or print(await actor1.score); either mean the same thing here.
    我们可以使用 await print(actor1.score) 或 print(await actor1.score);两者都在这里表示相同的含义。

The reason the await call is needed in copyScore(from:) is central to the reasons actors are needed at all.
在 copyScore(from:) 中需要 await 调用的原因是需要 actor 的核心原因。

You see, rather than just letting us poke around in an actor’s mutable state, Swift silently translates that request into what is effectively a message that goes into the actor’s message inbox: “please let me know your score as soon as you can.”
你看,Swift 不是让我们在 actor 的可变状态中四处闲逛,而是默默地将该请求转换为一条实际上进入 actor 消息收件箱的消息:“请尽快让我知道你的分数。

If the actor is currently idle it can respond with the score straight away and our code continues no different from if we had used a class or a struct. But the actor might also have multiple other messages waiting in its inbox that it needs to deal with first, so our score request has to wait. Eventually our request makes it to the top of the inbox and it will be dealt with, and the copyScore(from:) method will continue.
如果 actor 当前处于空闲状态,它可以立即使用分数进行响应,并且我们的代码继续与我们使用 class 或 struct 时没有什么不同。但是 actor 的收件箱中可能还有多条其他消息等待着它,它需要首先处理这些消息,因此我们的 score 请求必须等待。最终,我们的请求到达收件箱的顶部,它将被处理,并且 copyScore(from:) 方法将继续。

This means several things:
这意味着几件事:

  1. Actors are effectively operating a private queue for their message inbox, taking requests one at a time and trying to fulfill them. This executes requests in the order they were received, but you can also use task priority to escalate requests.
    actor 实际上是在为他们的消息收件箱作一个私有队列,一次接受一个请求并尝试完成它们。这将按照收到请求的顺序执行请求,但您也可以使用 task priority 来升级请求。
  2. Only one piece of code at a time can access an actor’s mutable state unless you specifically mark some things as being unprotected. Swift calls this actor isolation.
    一次只有一段代码可以访问 actor 的可变状态,除非你明确地将某些内容标记为不受保护的。Swift 将此 actor 称为 isolation
  3. Just like regular await calls, reading an actor’s property or method marks a potential suspension point – we might get a value back immediately, but it might also take a little time.
    就像常规的 await 调用一样,读取 actor 的属性或方法会标记一个潜在的暂停点 —— 我们可能会立即得到一个值,但也可能需要一点时间。
  4. Any state that is not mutable – i.e., a constant property – can be accessed without await, because it’s always going to be safe.
    任何可变的状态(即常量属性)都可以在没有 await 的情况下访问,因为它总是安全的。

In practice, the rule to remember is this: if you are writing code inside an actor’s method, you can read other properties on that actor and call its synchronous methods without using await, but if you’re trying to use that data from outside the actor await is required even for synchronous properties and methods. Think of situations where using self is possible: if you could self you don’t need await.
在实践中,要记住的规则是这样的:如果您在 actor 的方法中编写代码,您可以读取该 actor 上的其他属性并调用其同步方法,而无需使用 await,但如果您尝试从 actor 外部使用该数据,则即使对于同步属性和方法也需要 await。想想可以使用 self 的情况:如果你能 self,你就不需要 await

Writing properties from outside an actor is not allowed, with or without await.
不允许从 actor 外部写入属性,无论是否使用 await

Of course, the real question here is why Swift needs actors at all – what’s their fundamental purpose? And the answer is straightforward: if you ever need to make sure that access to some object is restricted to a single active task at a time, actors are perfect.
当然,这里真正的问题是为什么 Swift 需要 actor —— 它们的根本目的是什么?答案很简单:如果您需要确保对某个对象的访问一次仅限于单个活动任务,那么 actor 是完美的选择。

This is more common than you might think – yes, UI work should be restricted to the main thread, but you probably also want to restrict database access so that you can’t write conflicting data, for example, and so SwiftData has model actors.
这比您想象的更常见 – 是的,UI 工作应该仅限于主线程,但您可能还希望限制数据库访问,例如,您无法写入冲突的数据,因此 SwiftData 具有模型 actor。

There are also times when simultaneous concurrent access to data can cause data races – when the outcome of your work depends on the order in which tasks complete. These errors are particularly nasty to find and fix, but with actors such data races become impossible.
有时,同时并发访问数据可能会导致数据竞争 – 此时您的工作结果取决于任务的完成顺序。这些错误特别难以发现和修复,但对于 Actor 来说,这样的数据竞争变得不可能。

However, actor functions are reentrant, which is a computer science term that means one task can start while another one is still running. I know that sounds like it breaks the rules I laid out above, but it’s one of the subtle nuances of actors you’ll need to understand in order to use them effectively.
但是,actor 函数是可重入的,这是一个计算机科学术语,表示一个任务可以在另一个任务仍在运行时启动。我知道这听起来像是打破了我上面列出的规则,但这是你需要了解演员的细微差别之一,以便有效地使用它们。

Tip: Creating an instance of an actor has no extra performance cost compared to creating an instance of a class; the only performance difference comes when trying to access the protected state of an actor, which might trigger task suspension.
提示: 与创建类的实例相比,创建 actor 的实例没有额外的性能成本;唯一的性能差异是在尝试访问 Actor 的 protected 状态时出现的,这可能会触发任务暂停。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享