# 使用Send
和Sync
特性的可扩展并发
有趣的是,到目前为止我们在本章中讨论的几乎所有并发特性都是标准库的一部分,而不是语言本身的一部分。处理并发的选项不仅限于语言或标准库;你可以编写自己的并发特性或使用其他人编写的并发特性。
然而,嵌入在语言中而不是标准库中的关键并发概念之一是std::marker
特性Send
和Sync
。
# 使用Send
允许线程间所有权转移
Send
标记特性表示实现Send
的类型的值的所有权可以在线程之间转移。几乎所有的Rust类型都是Send,但也有一些例外,包括Rc<T>
:它不能实现Send
,因为如果你克隆了一个Rc<T>
值并尝试将克隆的所有权转移到另一个线程,两个线程可能会同时更新引用计数。因此,Rc<T>
被实现为在单线程情况下使用,在这种情况下你不想付出线程安全的性能代价。
因此,Rust的类型系统和特性约束确保你永远不会意外地不安全地跨线程发送Rc<T>
值。当我们在示例16-14中尝试这样做时,我们得到了the error the trait Send is not implemented for
的错误。当我们切换到实现了Rc<Mutex<i32>>
Send
的Arc<T>
时,代码编译通过了。
完全由Send
类型组成的任何类型也会自动标记为Send
。几乎所有的原始类型都是Send
,除了原始指针,我们将在第20章中讨论。
# 使用Sync
允许多线程访问
Sync
标记特性表示实现Sync
的类型可以安全地从多个线程引用。换句话说,任何类型T
如果&T
(对T
的不可变引用)实现了Send
,那么它就实现了Sync
,这意味着引用可以安全地发送到另一个线程。与Send
类似,所有原始类型都实现了Sync
,完全由实现Sync
的类型组成的类型也实现了Sync
。
智能指针Rc<T>
也不实现Sync
,原因与它不实现Send
相同。RefCell<T>
类型(我们在第15章中讨论过)和相关的Cell<T>
类型家族不实现Sync
。RefCell<T>
在运行时进行的借用检查实现不是线程安全的。智能指针Mutex<T>
实现了Sync
,可以用于在多个线程之间共享访问,正如你在"在多个线程之间共享Mutex
# 手动实现Send
和Sync
是不安全的
因为完全由实现Send
和Sync
特性的其他类型组成的类型也会自动实现Send
和Sync
,所以我们不必手动实现这些特性。作为标记特性,它们甚至没有任何方法需要实现。它们只是对于强制执行与并发相关的不变量很有用。
手动实现这些特性涉及实现不安全的Rust代码。我们将在第20章中讨论使用不安全的Rust代码;现在,重要的信息是构建不由Send
和Sync
部分组成的新并发类型需要仔细思考以维护安全保证。"The Rustonomicon" (opens new window)有关于这些保证以及如何维护它们的更多信息。
# 总结
这不是你在本文档中最后一次看到并发:下一章重点讨论异步编程,第21章中的项目将在比这里讨论的小例子更现实的情况下使用本章中的概念。
如前所述,因为Rust处理并发的方式很少是语言的一部分,所以许多并发解决方案都是作为crate实现的。这些比标准库发展得更快,所以一定要在网上搜索当前最先进的crate,以便在多线程情况下使用。
Rust标准库提供了用于消息传递的通道和智能指针类型,如Mutex<T>
和Arc<T>
,它们可以安全地用于并发上下文。类型系统和借用检查器确保使用这些解决方案的代码不会最终出现数据竞争或无效引用。一旦你的代码编译通过,你可以放心,它将愉快地在多个线程上运行,而不会出现其他语言中常见的难以追踪的错误。并发编程不再是一个令人恐惧的概念:勇往直前,无所畏惧地让你的程序并发吧!
← 共享状态并发 Future 与异步语法 →