具有 SyncSend 特征的可扩展并发


有趣的是,Rust 语言的并发功能非常少。到目前为止,我们在本章中讨论的几乎所有并发功能都是标准库的一部分,而不是语言的一部分。处理并发的选项不限于语言或标准库;您可以编写自己的并发功能,也可以使用其他人编写的功能。


但是,该语言中嵌入了两个并发概念: std::marker 特征 SyncSend


允许使用 send 在线程之间转移所有权


Send marker 特征指示实施 Send 类型的值的所有权可以在线程之间转移。几乎所有 Rust 类型都是 Send,但也有一些例外,包括 Rc<T>:这不能是 发送,因为如果您克隆了 Rc<T> 值并尝试将克隆的所有权转移给另一个线程,则两个线程可能会同时更新引用计数。因此,Rc<T> 被实现用于单线程情况,在这种情况下,您不想付出线程安全性能的损失。


因此,Rust 的类型系统和 trait 边界确保你永远不会意外地不安全地跨线程发送 Rc<T> 值。当我们在示例 16-14 中尝试执行此作时,我们收到了错误 the trait Send is not implemented for Rc<Mutex<i32>> .当我们切换到 Arc<T>即 Send)时,代码编译完成。


完全由 Send 类型组成的任何类型也会自动标记为 Send。几乎所有的 primitive 类型都是 Send,除了 raw 指针,我们将在 Chapter 19 中讨论。


允许使用 Sync 从多个线程访问


Sync marker trait 表示它对于实现 sync 以从多个线程引用。换句话说,任何类型 T 都是 Sync if &T (对 T 的不可变引用) 为 Send,这意味着该引用可以安全地发送到另一个线程。与 Send 类似,原始类型是 Sync 和完全由 Sync 类型组成的类型也是 Sync


智能指针 Rc<T> 也不是 Sync 的,原因与它不是 Sync 的原因相同 发送RefCell<T> 类型(我们在第 15 章中讨论过)和相关的 Cell<T> 类型系列不是同步的。RefCell<T> 在运行时执行的借用检查的实现不是线程安全的。智能指针 Mutex<T>Sync,可用于与多个线程共享访问权限,如您在“在多个线程之间共享 Mutex<T>”中所示 Threads“ 部分。


手动实现 SendSync 是不安全的


由于由 SendSync 特征组成的类型也会自动成为 SendSync,因此我们不必手动实施这些特征。作为 marker trait,它们甚至没有任何方法可以实现。它们仅用于强制执行与并发相关的不变量。


手动实现这些 trait 涉及实现不安全的 Rust 代码。我们将在第 19 章讨论使用不安全的 Rust 代码;目前,重要信息是构建不由 SendSync Parts 需要仔细考虑以维护安全保证。“这 Rustonomicon“提供了有关这些保证以及如何维护这些保证的更多信息。


总结


这并不是您在本书中看到的最后一个并发性:第 20 章中的项目将在比此处讨论的小示例更现实的情况下使用本章中的概念。


如前所述,因为 Rust 处理并发的方式很少是语言的一部分,所以许多并发解决方案都是作为 crate 实现的。这些比标准库发展得更快,因此请务必在线搜索当前最先进的 crate 以在多线程情况下使用。


Rust 标准库为消息传递和智能指针类型(如 Mutex<T>Arc<T>)提供了通道,这些通道可以在并发上下文中安全使用。类型系统和借用检查器确保使用这些解决方案的代码不会以数据争用或无效引用结束。一旦你让代码编译起来,你就可以放心了,它将愉快地在多个线程上运行,而不会出现其他语言中常见的难以追踪的 bug。并发编程不再是一个令人恐惧的概念:勇往直前,让你的程序并发,无所畏惧!


接下来,我们将讨论随着 Rust 程序的规模扩大而对问题进行建模和构建解决方案的惯用方法。此外,我们将讨论 Rust 的惯用语与你可能熟悉的面向对象编程的惯用语有什么关系。