SendSync 是两个十分相近的 trait,它们是一起保证了 Rust 的线程安全,它们又有什么异同点呢?

(Quick Question and Answer 系列旨在对小问题做简短解答)

  • Send 表示数据能安全地被 move 到另一个线程
  • Sync 表示数据能在多个线程中被同时安全地访问

这里“安全”指不会发生数据的竞争 (race condition)。

Send 代表没有数据共享

数据如果被 move 到另一个线程里,它还安全吗?能正常使用吗?如果可以,则说它是 Send

反例: Rc。我们知道 Rc 中保存了一个 reference count,记录有多少变量引用了当前的数据,当 reference count 归 0 时才释放(drop)数据本身。现在如果我们把一个 Rc move 到另一个线程里,尽管是 move,Rc 的实现还是决定了不同线程里的 Rc 会指向同一个 reference count,这意味着不同的线程可能同时修改 reference count,而 Rc 内部并没有实现同步机制,因此是不安全的。

这里有一个推论:一个结构(Struct)如果不满足 Send,是不是意味着它的某个内部数据不满足Sync 呢?参考 Rc 的例子,就是内部的 reference count 不满足 Sync 。只是目前没有找到相关的证明。

Sync 代表同步

如果多个线程同时访问某个数据,会不会产生竞争?如果还是安全的,我们就能说它是 Sync

反例:RefCell。它满足 Send,但不满足 SyncRefCell 不会与本线程的其它引用共享数据,所以被 move 到其它线程是安全的。但如果有多个线程同时拥有 RefCell 的引用,并同时获取它的可变引用(mutable reference)并尝试修改它,则会产生竞争,亦即没有满足原子性。

marker trait

最后要说的是,SendSync 都属于 marker trait,marker trait 的特点是不包含任何方法,所以为某个数据结构实现 marker trait 相当于人为告诉编译器,我实现的数据结构符合你的要求(如满足 Send, Sync),编译期间就放心吧。换句话说,编译器并无法检查你的实现到底是不是满足 SendSync,只能选择相信程序员的声明,如果程序员的实现有问题,只能程序员自己背锅了。

参考