深入理解Rust中的 Send 和 Sync trait
Rust中的標(biāo)記 trait(Marker Trait)Send 和 Sync 是并發(fā)安全的核心基石。用于標(biāo)記類型的并發(fā)安全屬性,讓編譯器在編譯時檢查線程間數(shù)據(jù)傳遞的合法性,從根源上避免數(shù)據(jù)競爭(Data Race)。
Send:類型的值能不能在線程之間“移動所有權(quán)”。Sync:類型的值能不能在多個線程中“共享引用 &T”。- 對同一個值進(jìn)行
并發(fā)訪問(只讀)是內(nèi)存安全的。 并發(fā)修改仍然要靠鎖/原子/其他機制。
- 對同一個值進(jìn)行

Send
一個類型 T 是 Send,表示把 T 的一個 值 從一個線程移動到另一個線程,不會引發(fā)數(shù)據(jù)競爭或內(nèi)存安全問題。
- 移動的是 所有權(quán),而不是引用。
- 移動之后,原線程不能再用這個值(所有權(quán)已經(jīng)被 move)。
絕大多數(shù)類型自動實現(xiàn) Send,以下一些例外(!Send):
Rc<T>:引用計數(shù)是非原子操作,跨線程轉(zhuǎn)移會導(dǎo)致計數(shù)競爭(數(shù)據(jù)競爭)。RefCell<T>/Cell<T>:內(nèi)部可變性無同步機制,跨線程轉(zhuǎn)移后,多個線程可能同時修改數(shù)據(jù)。- 裸指針
*const T/*mut T:無安全保證,直接跨線程轉(zhuǎn)移可能導(dǎo)致懸垂指針或數(shù)據(jù)競爭。 UnsafeCell<T>:內(nèi)部可變性的底層實現(xiàn),本身!Send(但基于它的線程安全類型如Mutex會手動實現(xiàn)Send)。
use std::thread;
fn main() {
let v = vec![1, 2, 3];
// Vec<i32> 是 Send,通過move把所有權(quán)移到線程中
let handle = thread::spawn(move || {
println!("{:?}", v);
});
handle.join().unwrap();
}sync
一個類型 T 是 Sync,表示可以安全地被多個線程共享(即 &T 是 Send)。即,如果一個類型的引用 &T 能安全跨線程傳遞,那么這個類型就是 Sync(多個線程持有 &T 不會導(dǎo)致數(shù)據(jù)競爭)。
任何允許通過 共享引用 &T 獲得可變訪問 且沒有同步保護(hù)的類型,都不是 Sync:
Cell<T>/RefCell<T>:允許通過&T改變內(nèi)部數(shù)據(jù)(“內(nèi)部可變性”),但不保證線程安全。Rc<T>:通過共享引用可以克隆Rc得到更多引用,從而在多線程下導(dǎo)致競爭。- 非線程安全的 FFI 對象,如果通過
&能調(diào)用會修改內(nèi)部狀態(tài)的方法。
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(5)); // Arc<Mutex<i32>>
let data_clone = data.clone();
thread::spawn(move || {
let mut guard = data_clone.lock().unwrap(); // 加鎖,獨占訪問
*guard += 1;
}).join().unwrap();
println!("{}", data.lock().unwrap()); // 輸出 6
}auto trait
Send 和 Sync 都是 auto trait,編譯器會根據(jù)類型的組成部分自動推導(dǎo)實現(xiàn)。
| 性質(zhì) | 含義 | 常見例子 |
|---|---|---|
Send + Sync | 可以在線程間移動,也可以多線程共享引用 | i32, Mutex<T>等 |
Send + !Sync | 可以在線程間移動,但不能多線程共享引用 | 一些 *mut T封裝,Sender<T> |
!Send + Sync | 不能跨線程移動;不移動,只讀共享是安全的(較少見) | 少數(shù)特殊 FFI 對象 |
!Send + !Sync | 既不能跨線程移動,也不能多線程共享引用 | Rc<RefCell<T>> 等 |
只有在特殊情況下(寫 底層庫 / FFI 封裝 / 自己的鎖和原子數(shù)據(jù)結(jié)構(gòu) 時)才會需要手動實現(xiàn)Send 和 Sync :
unsafe impl Send/unsafe impl Sync是非常嚴(yán)肅的承諾- 要自己保證所有可能的并發(fā)訪問路徑都不會產(chǎn)生數(shù)據(jù)競爭、懸垂指針等。
use std::sync::Mutex;
// 自定義類型:用 Mutex 保護(hù)裸指針(裸指針本身 !Send/!Sync)
struct SafePtr<T>(Mutex<*mut T>);
impl<T> SafePtr<T> {
fn new(value: T) -> Self {
let ptr = Box::into_raw(Box::new(value)); // 裸指針指向堆內(nèi)存
SafePtr(Mutex::new(ptr))
}
// 安全訪問:通過 Mutex 加鎖,保證獨占訪問
fn get(&self) -> Option<&T> {
let guard = self.0.lock().ok()?;
unsafe { (*guard).as_ref() } // 裸指針解引用需 unsafe,但鎖保證安全
}
}
// 手動實現(xiàn) Send/Sync:因為內(nèi)部用 Mutex 保護(hù),訪問裸指針是線程安全的
unsafe impl<T> Send for SafePtr<T> {}
unsafe impl<T> Sync for SafePtr<T> {}
// 測試:跨線程共享 SafePtr
fn main() {
let ptr = SafePtr::new(5);
let ptr_clone = Arc::new(ptr);
let ptr_clone2 = ptr_clone.clone();
thread::spawn(move || {
println!("線程內(nèi)訪問:{}", ptr_clone2.get().unwrap()); // 安全
}).join().unwrap();
}到此這篇關(guān)于深入理解Rust中的 Send 和 Sync trait的文章就介紹到這了,更多相關(guān)Rust Send 和 Sync trait內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解讀Rust的Rc<T>:實現(xiàn)多所有權(quán)的智能指針方式
Rc<T> 是 Rust 中用于多所有權(quán)的引用計數(shù)類型,通過增加引用計數(shù)來管理共享數(shù)據(jù),只有當(dāng)最后一個引用離開作用域時,數(shù)據(jù)才會被釋放,Rc<T> 適用于單線程環(huán)境,并且只允許不可變共享數(shù)據(jù);需要可變共享時應(yīng)考慮使用 RefCell<T> 或其他解決方案2025-02-02
Rust聲明宏在不同K線bar類型中的應(yīng)用小結(jié)
在K線bar中,往往有很多不同分時k線圖,比如1,2,3,5,,,,,60,120,250,300…,,不同分鐘類型,如果不用宏,那么手寫會比較麻煩,下面就試用一下宏來實現(xiàn)不同類型的bar,感興趣的朋友一起看看吧2024-05-05
Rust應(yīng)用調(diào)用C語言動態(tài)庫的操作方法
這篇文章主要介紹了Rust應(yīng)用調(diào)用C語言動態(tài)庫,本文記錄了筆者編寫一個簡單的C語言動態(tài)庫,并通過Rust調(diào)用動態(tài)庫導(dǎo)出的函數(shù),需要的朋友可以參考下2023-01-01

