Skip to content

28.2 什么是 Sync

对应的,Sync 的定义是,如果类型 T 实现了 Sync trait,那说明在不同的线程中使用&T访问同一个变量是安全的。 这句话也不好理解。下面我们仔细分析一下哪些类型是满足 Sync 约束的。

显然,基本数字类型肯定是 Sync。 假如不同线程都拥有指向同一个 i32 类型的只读引用&i32,这是没什么问题的。因为这个类型引用只能读,不能写。多个线程读同一个整数是安全的。

大部分具有泛型参数的类型是否满足 Sync,很多都是取决于参数类型是否满足 Sync。 像Box<T>Vec<T>Option<T>这种也是 Sync 的,只要其中的参数 T 是满足 Sync 的。

也有一些类型,不论泛型参数是否满足 Sync,它都是满足 Sync 的。这种类型把不满足 Sync 条件的类型用它包起来,就变成了满足 Sync 条件的。 Mutex<T>就是这种。多个线程同时拥有&Mutex<T>型引用,指向同一个变量是没问题的。

那么什么样的类型是!Sync呢?所有具有“内部可变性”而又没有多线程同步考虑的类型都不是 Sync 的。比如,Cell<T>RefCell<T>就不能是 Sync 的。 按照定义,如果我们多个线程中都持有指向同一个变量的&Cell<T>型指针,那么在多个线程中,都可以执行Cell::set方法来修改它里面的数据。而我们知道,这个方法在修改内部数据的时候,是没有考虑多线程同步问题的。 所以,我们必须把它标记为!Sync

还有一些特殊的类型,它们既具备内部可变性,又满足 Sync 约束,比如前面提到的Mutex<T>类型。为什么说Mutex<T>具备内部可变性? 大家查一下文档就会知道,这个类型可以通过不可变引用调用lock()方法,返回一个智能指针MutexGuard<T>类型,而这个智能指针有权修改内部数据。 这个做法就跟RefCell<T>try_borrow_mut()方法非常类似。区别只是:Mutex::lock()方法的实现,使用了操作系统提供的多线程同步机制,实现了线程同步,保证了异步安全;而 RefCell 的内部实现就是简单的普通数字加减操作。 因此,Mutex<T>既具备内部可变性,又满足 Sync 约束。除了Mutex<T>,标准库中还有RwLock<T>AtomicBoolAtomicIsizeAtomicUsizeAtomicPtr等类型,都提供了内部可变性,而且满足 Sync 约束。

Released under the MIT License