Arc in Rust
前言
在Rust語言中,所有權大多數情況下是明確的,對於一個給定的值,你可以準確地判斷出,哪個變數擁有它。但是也存在一些情況,單個值可能同時被多個所有者持有。
請看下面的例子:
fn process_files_in_parallel(filenames: Vec<string>, glossary: &GigabyteMap) -> io::Result<()> { ... for worklist in worklists { thread_handlers.push( spawn(move || process_files(worklist, glossary) ); } .... } `
上面的例子中,擬採用多個程序,併發地處理檔案。但是處理檔案需要一個消耗巨大記憶體的資料結構, glossary,可能資料結構大小超過GB,這種情況下,拷貝多個副本給不同執行緒使用無疑是無法承受的。但是上面的程式碼,存在一個問題,即glossary的生命週期。
因為glossary並不能保證在所有的子程序完成任務之前不被銷燬,如果被銷燬,會導致子程序無法正常完成任務。
Rc and Arc
Rust提供了一個名為Rc
那Arc是幹啥的呢?Arc具備Rc的一切特徵,那是Rc不是執行緒安全的,所以Rust又提供了一個Arc
那看起來是Arc是更強大的Rc,能夠保證安全地用在併發的場景,那為什麼不乾脆消滅掉Rc,統統只使用Arc呢?
沒有免費的午餐,Arc更強大,但是這種強大,需要付出一些效能開銷才能做到,如果是單執行緒條件下,我們其實不需要付出這種開銷。因此
- 單執行緒條件下,可以使用Rc
- 多執行緒併發條件下,建議使用Arc
use std::thread; use std::time::Duration; use std::sync::Arc; fn main() { let foo = Arc::new(vec![0]); for _ in 0..10 { let bar = Arc::clone(&foo); thread::spawn(move || { thread::sleep(Duration::from_millis(200)); println!("{:?}", &bar); }); } println!("{:?}", foo); }
上面的用法中, Arc::clone 並不是複製了一份vec,而是僅僅增減了一個引用計數,這是多個執行緒之間共享資料的一個方法。
回到我們最開始的問題,巨大的資料結構,多個執行緒之間共享:
fn process_files_in_parallel(filenames: Vec<String>, glossary: Arc<GigabyteMap>) -> io::Result<()> { .... for worklist in worklists { let glossary_for_child = glossary.clone() ; thread_handlers.push( spawn(move || process_files(worklist, &glossary_for_child)) ); } .... }