5分钟速读之Rust权威指南(三十三)Send&Sync

363 阅读2分钟

Send trait和Sync trait

前面几节过后,并发的基本使用就聊完了,最后还剩下一点,前面我们在创建线程后,允许将数据的所有权转移到线程中(比如:i32,String,Vec<T>, Arc<T>等),但是并非所有数据都可以转移,比如当我们前面尝试将Rc<T>所有权转移到线程中时就会报错:

use std::rc::Rc;
use std::thread;

let a = Rc::new(1);
let a2 = a.clone();
thread::spawn(|| {
  a2; // 报错,Rc<i32>不能在线程之间安全地发送
});

实际上在于Rc<T>没有Send trait,而rust大部分类型都实现了这个trait。

Send trait允许线程间转移所有权

只有实现了Send trait的类型才可以安全地在线程间转移所有权,除了Rc<T>等极少数的类型,几乎所有的Rust类型都实现了Send trait。

如果我们将克隆后的Rc<T>值的所有权转移到了另外一个线程中,那么两个线程就有可能同时更新引用计数值并进而导致计数错误。

Sync trait允许多线程同时访问

只在线程中传递是不够的的,还需要我们在线程中能够对数据进行访问,比如前面用过的Mutex<T>就可以在线程中访问,而RefCell<T>就不可以:

use std::cell::RefCell;
use std::thread;

let a = RefCell::new(1);
thread::spawn(|| {
  a.borrow_mut(); // 报错,RefCell<i32>不能在线程之间安全共享
});

只有实现了Sync trait的类型才可以安全地被多个线程引用,类型Mutex<T>是Sync的,可以被多个线程共享访问,类型RefCell<T>不满足Sync约束,实现的运行时借用检查并没有提供有关线程安全的保证。

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

当某个类型完全由实现了Send与Sync的类型组成时,它就会自动实现Send与Sync。

我们并不需要手动地为此种类型实现相关trait。这两个被称作”标签trait“,Send与Sync其实并没有任何可供实现的方法。它们仅仅被用来标识并发相关的不可变性。