Send 和 Sync
std::marker 模塊中,有兩個 trait:Send 和 Sync,它們與多線程安全相關。
標記為 marker trait 的 trait,它實際就是一種約定,沒有方法的定義,也沒有關聯元素(associated items)。僅僅是一種約定,實現了它的類型必須滿足這種約定。一種類型是否加上這種約定,要麼是編譯器的行為,要麼是人工手動的行為。
Send 和 Sync 在大部分情況下(針對 Rust 的基礎類型和 std 中的大部分類型),會由編譯器自動推導出來。對於不能由編譯器自動推導出來的類型,要使它們具有 Send 或 Sync 的約定,可以由人手動實現。實現的時候,必須使用 unsafe 前綴,因為 Rust 默認不信任程序員,由程序員自己控制的東西,統統標記為 unsafe,出了問題(比如,把不是線程安全的對象加上 Sync 約定)由程序員自行負責。
它們的定義如下:
如果 T: Send,那麼將 T 傳到另一個線程中時(按值傳送),不會導致數據競爭或其它不安全情況。
Send是對象可以安全發送到另一個執行體中;Send使被髮送對象可以和產生它的線程解耦,防止原線程將此資源釋放後,在目標線程中使用出錯(use after free)。
如果 T: Sync,那麼將 &T 傳到另一個線程中時,不會導致數據競爭或其它不安全情況。
Sync是可以被同時多個執行體訪問而不出錯;Sync防止的是競爭;
推論:
T: Sync意味著&T: Send;Sync + Copy = Send;- 當
T: Send時,可推導出&mut T: Send; - 當
T: Sync時,可推導出&mut T: Sync; - 當
&mut T: Send時,不能推導出T: Send;
(注:T, &T, &mut T,Box<T> 等都是不同的類型)
具體的類型:
- 原始類型(比如: u8, f64),都是
Sync,都是Copy,因此都是Send; - 只包含原始類型的複合類型,都是
Sync,都是Copy,因此都是Send; - 當
T: Sync,Box<T>,Vec<T>等集合類型是Sync; - 具有內部可變性的的指針,不是
Sync的,比如Cell,RefCell,UnsafeCell; Rc不是Sync。因為只要一做&Rc<T>操作,就會克隆一個新引用,它會以非原子性的方式修改引用計數,所以是不安全的;- 被
Mutex和RWLock鎖住的類型T: Send,是Sync的; - 原始指針(
*mut,*const)既不是Send也不是Sync;
Rust 正是通過這兩大武器:所有權和生命週期 + Send 和 Sync(本質上為類型系統)來為併發編程提供了安全可靠的基礎設施。使得程序員可以放心在其上構建穩健的併發模型。這也正是 Rust 的核心設計觀的體現:內核只提供最基礎的原語,真正的實現能分離出去就分離出去。併發也是如此。