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 的核心設計觀的體現:內核只提供最基礎的原語,真正的實現能分離出去就分離出去。併發也是如此。