1 - Sync trait的std文档
https://doc.rust-lang.org/std/marker/trait.Sync.html
pub unsafe auto trait Sync { }
可以在线程之间安全共享引用的类型。
当编译器确定适当时,会自动实现此 trait。
精确的定义是:当且仅当 &T
是 Send
时,类型 T
才是 Sync
。 换句话说,如果在线程之间传递 &T
引用时没有 未定义的行为 (包括数据竞争) 的可能性。
正如人们所料,像 u8
和 f64
这样的原始类型都是 Sync
,包含它们的简单聚合类型也是如此,比如元组、结构体和枚举。 基本 Sync
类型的更多示例包括不可变类型 (例如 &T
) 以及具有简单继承的可变性的类型,例如 Box
,Vec
和大多数其他集合类型。
该定义的一个令人惊讶的结果是 &mut T
是 Sync
(如果 T
是 Sync
),即使看起来可能提供了不同步的可变的。 诀窍是,共享引用 (即 & &mut T
) 后面的可变引用将变为只读,就好像它是 & &T
一样。 因此,没有数据竞争的风险。
不是 Sync
的类型是具有非线程安全形式的 “内部可变性” 的类型,例如 Cell
和 RefCell
。 这些类型甚至允许通过不可变,共享引用来更改其内容。 例如,Cell
上的 set
方法采用 &self
,因此它仅需要共享的引用 &Cell
。 该方法不执行同步,因此 Cell
不能为 Sync
。
另一个非 Sync
类型的例子是引用计数指针 Rc
。 给定任何引用 &Rc
,您可以克隆新的 Rc
,以非原子方式修改引用计数。
对于确实需要线程安全的内部可变性的情况,Rust 提供 原子数据类型 以及通过 sync::Mutex
和 sync::RwLock
进行的显式锁定。 这些类型可确保任何可变的都不会引起数据竞争,因此类型为 Sync
。 同样,sync::Arc
提供了 Rc
的线程安全模拟。
任何具有内部可变性的类型还必须在 value(s) 周围使用 cell::UnsafeCell
包装器,该包装器可以通过共享的引用进行更改。 未定义的行为 无法做到这一点。 例如,从 &T
到 &mut T
的 transmute
无效。
有关 Sync
的更多详细信息,请参见 the Nomicon。
2 - Sync trait的源码
https://github.com/rust-lang/rust/blob/master/library/core/src/marker.rs
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "sync_trait")]
#[lang = "sync"]
#[rustc_on_unimplemented(
message = "`{Self}` cannot be shared between threads safely",
label = "`{Self}` cannot be shared between threads safely"
)]
pub unsafe auto trait Sync {
// Empty
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for *const T {}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for *mut T {}