您好,登錄后才能下訂單哦!
這篇文章給大家介紹Arc在Rust中是怎么工作的,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
原子引用計數(Arc)類型是一種智能指針,它能夠讓你以線程安全的方式在線程間共享不可變數據。我還沒有發現能夠很好地解釋它的工作原理的文章,所以我決定嘗試來寫一篇。
第一部分是介紹怎樣使用Arc和為什么要使用Arc;如果你已經了解這部分內容,只是想知道它是如何工作的,可以直接跳到第二部分:“它是怎樣工作的(How does it work)”。
為什么你需要使用Arc?當你試圖在線程間共享數據時,需要Arc類型來保證被共享的類型的生命周期,與運行時間最長的線程活得一樣久。考慮下面的例子:
use std::thread;use std::time::Duration;fn main() { let foo = vec![0];
// creation of foo here thread::spawn(|| {
thread::sleep(Duration::from_millis(20)); println!("{:?}", &foo); });}
// foo gets dropped here
// wait 20 milliseconds
// try to print foo
這段代碼無法編譯通過。我們會得到一個錯誤,稱foo的引用活得比foo自身更久。這是因為foo在main函數結尾處就被丟棄(drop)了,并且這個被丟棄的值會在20毫秒后在生成的線程中被試圖訪問。這就是Arc的作用所在。原子引用計數確保在對foo類型的所有引用都結束之前,它不會被丟棄——因此即使在main函數結束之后,foo仍然會存在。現在考慮下面的示例:
use std::thread;use std::sync::Arc;use std::time::Duration;fn main() {
let foo = Arc::new(vec![0]); let bar = Arc::clone(&foo);
thread::spawn(move || {
thread::sleep(Duration::from_millis(20)); println!("{:?}", *bar); });
println!("{:?}", foo);}
在這個例子中,我們可以在(主)線程中引用foo并且還可以在(子)線程被生成之后訪問它的值。
它是怎樣工作的?你已經知道如何使用Arc了,現在讓我們討論一下它是如何工作的。當你調用let foo = Arc::new(vec![0])時,你同時創建了一個vec![0]和一個值為1的原子引用計數,并且把它們都存儲在堆上的相同位置(緊挨著)。指向堆上的這份數據的指針存放在foo中。因此,foo是由指向一個對象的指針構成,被指向的對象包含vec![0]和原子計數。
當你調用let bar = Arc::clone(&foo)時,你是在獲取foo的一個引用、對foo(指向存放在堆上的數據的指針)解引用、接著找到foo指向的地址、找出里面存放的值(vec![0]和原子計數)、把原子計數加一、最后把指向vec![0]的指針保存在bar中。
當foo或bar離開作用域時,Arc::drop()就被調用了,原子計數減一。如果Arc::drop()發現原子計數等于0,那么它所指向的堆上的數據(vec![0]和原子計數)會被清理并從堆上擦除。
原子計數是一種能夠讓你以線程安全的方式修改和增加它的值的類型;在對原子類型允許進行其他操作之前,前面的原子類型操作必須要全部完成;因此被稱為原子的(atomic)(即不可分割的)(操作)。
需要注意的是,Arc只能包含不可變數據。這是因為如果兩個線程試圖在同一時間修改被包含的值,Arc無法保證避免數據競爭。如果你希望修改數據,你應該在Arc類型內部封裝一個互斥鎖保護(Mutex guard)。
為什么這些東西能讓Arc是線程安全的呢?Arc是線程安全的是因為它給編譯器保證數據的引用至少活得和數據本身一樣長(譯注:這里原作者應該是想表達,數據的引用存在期間,數據都是有效的)。這是因為每次你創建一個對堆上數據得引用,原子計數就會加一,數據只有在當原子計數等于零得時候才會被丟棄(每當一個引用離開作用域時,原子計數會減一)——Arc和一個普通得Rc(引用計數)之間得區別就在于原子計數。
關于Arc在Rust中是怎么工作的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。