# 可扩展的并发：`Sync`和`Send` - Rust 程序设计语言 简体中文版
Rust 程序设计语言 简体中文版

## 使用`Sync`和`Send` trait 的可扩展并发

> ch16-04-extensible-concurrency-sync-and-send.md
> commit 9430a3d28a2121a938d704ce48b15d21062f880e

Rust 的并发模型中一个有趣的方面是：语言本身对并发知之甚少。我们之前讨论的几乎所有内容，都属于标准库，而不是语言本身的内容。由于不需要语言提供并发相关的基础设施，并发方案不受标准库或语言所限：我们可以编写自己的或使用别人编写的。

我们说“几乎所有内容都不属于语言本身”，那么属于语言本身的是什么呢？是两个 trait，都位于`std::marker`： `Sync`和`Send`。

### `Send`用于表明所有权可能被传送给其他线程

`Send`标记 trait 表明类型的所有权可能被在线程间传递。几乎所有的 Rust 类型都是`Send`的，不过有一些例外。比如标准库中提供的 `Rc<T>`：如果克隆`Rc<T>`值，并尝试将克隆的所有权传递给另一个线程，这两个线程可能会同时更新引用计数。正如上一部分提到的，`Rc<T>`被实现为用于单线程场景，这时不需要为拥有线程安全的引用计数而付出性能代价。

因为 `Rc<T>` 没有标记为 `Send`，Rust 的类型系统和 trait bound 会确保我们不会错误的把一个 `Rc<T>` 值不安全的在线程间传递。列表 16-14 曾尝试这么做，不过得到了一个错误，`the trait Send is not implemented for Rc<Mutex<i32>>`。而使用标记为 `Send` 的 `Arc<T>` 时，就没有问题了。

任何完全由 `Send` 的类型组成的类型也会自动被标记为 `Send`：几乎所有基本类型都是 `Send` 的，大部分标准库类型是`Send`的，除了`Rc<T>`，以及第十九章将会讨论的裸指针（raw pointer）。

### `Sync` 表明多线程访问是安全的

`Sync` 标记 trait 表明一个类型可以安全的在多个线程中拥有其值的引用。换一种方式来说，对于任意类型 `T`，如果`&T`（`T`的引用）是`Send`的话`T`就是`Sync`的，这样其引用就可以安全的发送到另一个线程。类似于 `Send` 的情况，基本类型是 `Sync` 的，完全由 `Sync` 的类型组成的类型也是 `Sync` 的。

`Rc<T>` 也不是 `Sync` 的，出于其不是`Send`的相同的原因。`RefCell<T>`（第十五章讨论过）和`Cell<T>`系列类型不是`Sync`的。`RefCell<T>`在运行时所进行的借用检查也不是线程安全的。`Mutex<T>`是`Sync`的，正如上一部分所讲的它可以被用来在多线程中共享访问。

### 手动实现`Send`和`Sync`是不安全的

通常并不需要实现`Send`和`Sync` trait，由属于`Send`和`Sync`的类型组成的类型，自动就是`Send`和`Sync`的。因为他们是标记 trait，甚至都不需要实现任何方法。他们只是用来加强并发相关的不可变性的。

实现这些标记 trait 涉及到编写不安全的 Rust 代码，第十九章将会讲述具体的方法；当前重要的是，在创建新的由不是`Send`和`Sync`的部分构成的并发类型时需要多加小心，以确保维持其安全保证。The Nomicon 中有更多关于这些保证以及如何维持他们的信息。

## 总结

这不会是本书最后一个出现并发的章节；第二十章的项目会在更现实的场景中使用这些概念，而不像本章中讨论的这些小例子。

正如我们提到的，因为 Rust 本身很少有处理并发的部分内容，有很多的并发方案都由 crate 实现。他们比标准库要发展的更快；请在网上搜索当前最新的用于多线程场景的 crate。

Rust 提供了用于消息传递的通道，和像`Mutex<T>`和`Arc<T>`这样可以安全的用于并发上下文的智能指针。类型系统和借用检查器会确保这些场景中的代码，不会出现数据竞争和无效的引用。一旦代码可以编译了，我们就可以坚信这些代码可以正确的运行于多线程环境，而不会出现其他语言中经常出现的那些难以追踪的 bug。并发编程不再是什么可怕的概念：无所畏惧地并发吧！

接下来，让我们讨论一下当 Rust 程序变得更大时，有哪些符合语言习惯的问题建模方法和结构化解决方案，以及 Rust 的风格是如何与面向对象编程（Object Oriented Programming）中那些你所熟悉的概念相联系的。