/* Page prose (zh-Hans) */
"OneTaskAtATime" = "# 一个 actor 一次只允许一个任务进入\n\n首先，再看一遍这个问题。这是[并发的陷阱](Concurrency%20Hazards.playgroundchapter/Overview)那一章里那个 bug 的样子，以最简单的形态呈现。一个普通的 class 持有一个余额，许多并发的存款在它上面竞争：\n\n```swift\nfinal class UnsafeLedger {\n    var balance = 0\n\n    func deposit(_ amount: Int) {\n        balance += amount   // read, add, write (not atomic)\n    }\n}\n```\n\n那句 `balance += amount` 其实是*三*个步骤：读取当前值、加上去、再写回去。如果两个任务在同一时刻都读到 `0`，它们都会写入 `1`，于是一次存款就丢失了。运行一千次并发的存款，你很少能得到 `1000`。\n\n在 Swift 6 中，如果你真的试图在并发任务之间共享那个 `UnsafeLedger`，**编译器会拦住你**：一个普通的可变 class 不能安全地共享，所以它无法编译。这正是编译器在为你指出正确的工具。这个工具就是 actor。\n\n**actor** 是一种引用类型，就像 class 一样，但它的可变状态一次只能被**一个任务**触碰。actor 会把对自身状态的每一次访问都串行化，所以上面的读-加-写永远不会重叠。把 `class` 换成 `actor`（顺便，趁我们收紧规矩，把 `balance` 标记为 `private(set)`，这样只有 actor 自己的方法才能改变它；外部代码仍然可以读取它）：\n\n```swift\nactor Ledger {\n    private(set) var balance = 0\n\n    func deposit(_ amount: Int) {\n        balance += amount\n    }\n}\n```\n\n这就是全部的修复。现在 `deposit` 的调用会自动串行化：当一个任务在 `deposit` 内部时，其他每个任务都得等待轮到自己。不用设置队列，也没有什么要记住的。让我们用一个任务组来猛砸它一千次并发存款（来自 [Task Groups](Task%20Groups.playgroundchapter/Overview) 那一章）。";
"OneTaskAtATime-2" = "**观察运行：**最终余额是 **`1000`。每一次都是。** 一遍又一遍地运行它；它从不漂移。一千次存款全都到账了。把它和 class 版本对比一下，后者会不可预测地丢失更新。actor 把一场竞争变成了一个保证，而你一个锁都没写。\n\n* callout(试一试): 把循环提到 `1...10_000` 次存款。最终余额还会在每次运行时都精确无误吗？\n\n> 这和本书前面那个串行队列的修复不同。在那里你*选择*把每一次访问都路由到一个队列，没有什么强制你保持一致；一次走岔的直接访问就会重新引入竞争。actor 让那份保护成为类型的一部分。没有任何办法能绕过串行化去访问 `balance`，而且编译器会为你检查它。\n\n[上一页](@previous)  |  第 2 / 7 页  |  [下一页: actor 隔离](@next)";
