2025-02-10 09:29:10
,某些文章具有时效性,若有错误或已失效,请在下方留言。Actor-isolated functions are reentrant, which means it’s possible for one piece of work to begin before a previous piece of work completes. This opens the possibility of subtle bugs, so if you intend to use actors you should read this chapter carefully.
Actor 隔离的函数是可重入的,这意味着一项工作可能在前一项工作完成之前开始。这带来了细微错误的可能性,因此如果您打算使用 actor,则应仔细阅读本章。
First, you already know that whenever you see the await
keyword it marks a potential suspension point: once your code resumes after the await
, the state of your program might have changed. Actor reentrancy means this is true of actors too: if you use await
inside an actor method, that whole piece of work might suspend, and potentially another piece of work will begin while the first task is suspended.
首先,您已经知道,每当看到 await
关键字时,它都会标记一个潜在的挂起点:一旦代码在 await
之后恢复,程序的状态可能已更改。Actor 可重入性意味着 Actor 也是如此:如果你在 Actor 方法中使用 await
,那么整个工作可能会暂停,并且可能在第一个任务暂停时,另一项工作将开始。
You can see actor reentrancy in action with this simple code:
您可以通过以下简单代码查看 actor 重入性的实际应用:
actor Player {
var name = "Anonymous"
var score = 0
func addToScore() {
Task {
score += 1
try? await Task.sleep(for: .seconds(1))
print("Score is now \(score)")
}
}
}
let player = Player()
await player.addToScore()
await player.addToScore()
await player.addToScore()
try? await Task.sleep(for: .seconds(1.1))
In there we have one actor doing the same task three times, but each time it goes to sleep for a second using an await
call.
在那里,我们有一个 actor 执行相同的任务 3 次,但每次它使用 await
调用进入休眠状态一秒钟。
And so, the execution will be something like this:
因此,执行将如下所示:
addToScore()
starts running, and adds 1 toscore
. At this point,score
is 1.addToScore()
开始运行,并将score
加 1。此时,score
为 1。- It then goes to sleep for one second, at which point the actor is free to start another task.
然后它进入休眠状态一秒钟,此时 actor 可以自由地开始另一个任务。 addToScore()
starts running a second time, adding another 1 toscore
making it 2.addToScore()
开始第二次运行,再增加 1 到score
,使其为 2。- The second run then also goes to sleep, and again the actor is free to start new work.
然后,第二次运行也进入休眠状态,并且 actor 可以再次自由地开始新的工作。 addToScore()
starts running a third time, adding another 1 toscore
to make 3.addToScore()
开始第三次运行,再加 1 到score
得到 3。- The third run goes to sleep, just like the others.
第三次运行进入睡眠状态,就像其他运行一样。 - The first task finishes its sleep, at which it prints score with its current value of 3.
第一个任务完成其睡眠,此时它会打印当前值为 3 的 score。 - The second task then finishes sleeping, and prints 3.
然后,第二个任务完成休眠,并打印 3。 - The third task does the same.
第三个任务执行相同的作。 - We have one final sleep for 1.1 seconds, to make sure the actor finishes all its work before the program run terminates.
我们最后一次休眠 1.1 秒,以确保 actor 在程序运行终止之前完成所有工作。
So, when the code runs, it will print “Score is now 3” three times, because by the time all three tasks awaken that’s what the score
value will be.
因此,当代码运行时,它将打印 “Score is now 3” 三次,因为当所有三个任务都唤醒时,这就是 score
值的大小。
Swift calls this process is called interleaving, and it’s only possible wherever a suspension point is reached – regular synchronous code will run in its entirety without any suspension points, and therefore can’t be hit by actor reentrance.
Swift 将此过程称为交错,并且只有在到达暂停点时才有可能 – 常规同步代码将完全运行,没有任何暂停点,因此不会被 actor 重新进入命中。
The real rule we’re dealing with when using actors is this: no two tasks will run in parallel, meaning executing at same time. Several tasks could in theory be being handled by the actor right now, but no more than one can run simultaneously.
我们在使用 actor 时要处理的真正规则是:没有两个任务会并行运行,这意味着同时执行。理论上,Actor 现在可以处理多个任务,但不能同时运行多个任务。
This isn’t a bug in actors, but instead an intentional performance solution because it guarantees that your actor will make some progress on work available to it. This in turn helps to avoid a problem known as deadlocks, where two tasks are unable to proceed because each is waiting for the other to release a resource they need to continue.
这不是 actor 中的 bug,而是一种有意的表演解决方案,因为它保证你的 actor 将在可用的工作上取得一些进展。这反过来有助于避免称为死锁的问题,即两个任务无法继续,因为每个任务都在等待另一个任务释放它们需要继续的资源。
Important: If you both need to restrict access to state and also need to avoid actor reentrancy, it’s tempting to add some kind of manual locking inside an actors. However, that’s almost certainly a very bad idea because it’s just not how actors were designed – creating your own separate serial queue is probably what you want.
重要: 如果你既需要限制对 state 的访问,又需要避免 actor 的重入,那么在 actor 中添加某种手动锁定是很诱人的。然而,这几乎可以肯定是一个非常糟糕的主意,因为这不是 actors 的设计方式——创建你自己的单独串行队列可能是你想要的。