024 Rust死靈書之Send和Sync

linghuyichong發表於2021-05-07

本系列錄製的影片主要放在B站上Rust死靈書學習影片

Rust 死靈書相關的原始碼資料在github.com/anonymousGiga/Rustonomi...

當同一塊記憶體有多個別名,同時還可以改變記憶體的值的時候,它們就不是執行緒安全的。

Rust中根據Send和Sync trait獲取相關的資訊:

1、如果一個型別可以安全地傳遞給另一個執行緒,那麼這個型別是實現了Send這個trait了的;
2、如果一個型別可以安全地被多個執行緒共享,那麼這個型別就是Sync的。

Send和Sync是Rust併發機制的基礎,但是它們是非安全的trait。Send和Sync是標誌trait(即沒有任何關聯方法),型別要實現它們其實就是滿足它們需要的內部特徵。不正確的實現Send和Sync會導致未定義行為。

Send和Sync是自動推到的trait,規則:

1、如果一個型別完全由Send或者Sync組成,那麼這個型別本身也是Send或者Sync的;
2、幾乎所有的基本型別都是Send和Sync的。

例外情況:

1、裸指標不是Send的,也不是Sync的;
2、UnsafeCell不是Sync的(Cell和RefCell也不是);
3、Rc不是Send或Sync的(引用計數是共享且非同步的)。

Rc和UnsafeCell是典型的非執行緒安全的,因為它們允許非同步地共享可變狀態。

不能自動推導的型別也可以很容易的實現Send和Sync:

struct MyBox(*mut u8);

unsafe impl Send for MyBox {}
unsafe impl Sync for MyBox {}

還有一個比較少見的場景,一個型別被自動推導為Send或者Sync,但它其實不滿足二者的要求,此時我們可以為其去掉Send和Sync的實現,方式如下:

#![feature(negative_impls)]

// I have some magic semantics for some synchronization primitive!
struct SpecialThreadToken(u8);

impl !Send for SpecialThreadToken {}
impl !Sync for SpecialThreadToken {}

這裡需要注意的是,一種型別自己不可能被不正確的推導為Send和Sync,只有型別和其它非安全程式碼一起實現一些特殊行為時,才可能成為一個不正確的Send或者Sync

本作品採用《CC 協議》,轉載必須註明作者和本文連結
令狐一衝

相關文章