Rust 使用 Sync 和 Send trait 的可擴(kuò)展并發(fā)

2023-03-22 15:15 更新
ch16-04-extensible-concurrency-sync-and-send.md
commit a7a6804a2444ee05ff8b93f54973a9ce0f6511c1

Rust 的并發(fā)模型中一個(gè)有趣的方面是:語言本身對(duì)并發(fā)知之 甚少。我們之前討論的幾乎所有內(nèi)容,都屬于標(biāo)準(zhǔn)庫,而不是語言本身的內(nèi)容。由于不需要語言提供并發(fā)相關(guān)的基礎(chǔ)設(shè)施,并發(fā)方案不受標(biāo)準(zhǔn)庫或語言所限:我們可以編寫自己的或使用別人編寫的并發(fā)功能。

然而有兩個(gè)并發(fā)概念是內(nèi)嵌于語言中的:std::marker 中的 Sync 和 Send trait。

通過 Send 允許在線程間轉(zhuǎn)移所有權(quán)

Send 標(biāo)記 trait 表明實(shí)現(xiàn)了 Send 的類型值的所有權(quán)可以在線程間傳送。幾乎所有的 Rust 類型都是Send 的,不過有一些例外,包括 Rc<T>:這是不能 Send 的,因?yàn)槿绻寺×?nbsp;Rc<T> 的值并嘗試將克隆的所有權(quán)轉(zhuǎn)移到另一個(gè)線程,這兩個(gè)線程都可能同時(shí)更新引用計(jì)數(shù)。為此,Rc<T> 被實(shí)現(xiàn)為用于單線程場(chǎng)景,這時(shí)不需要為擁有線程安全的引用計(jì)數(shù)而付出性能代價(jià)。

因此,Rust 類型系統(tǒng)和 trait bound 確保永遠(yuǎn)也不會(huì)意外的將不安全的 Rc<T> 在線程間發(fā)送。當(dāng)嘗試在示例 16-14 中這么做的時(shí)候,會(huì)得到錯(cuò)誤 the trait Send is not implemented for Rc<Mutex<i32>>。而使用標(biāo)記為 Send 的 Arc<T> 時(shí),就沒有問題了。

任何完全由 Send 的類型組成的類型也會(huì)自動(dòng)被標(biāo)記為 Send。幾乎所有基本類型都是 Send 的,除了第十九章將會(huì)討論的裸指針(raw pointer)。

Sync 允許多線程訪問

Sync 標(biāo)記 trait 表明一個(gè)實(shí)現(xiàn)了 Sync 的類型可以安全的在多個(gè)線程中擁有其值的引用。換一種方式來說,對(duì)于任意類型 T,如果 &TT 的不可變引用)是 Send 的話 T 就是 Sync 的,這意味著其引用就可以安全的發(fā)送到另一個(gè)線程。類似于 Send 的情況,基本類型是 Sync 的,完全由 Sync 的類型組成的類型也是 Sync 的。

智能指針 Rc<T> 也不是 Sync 的,出于其不是 Send 相同的原因。RefCell<T>(第十五章討論過)和 Cell<T> 系列類型不是 Sync 的。RefCell<T> 在運(yùn)行時(shí)所進(jìn)行的借用檢查也不是線程安全的。Mutex<T> 是 Sync 的,正如 “在線程間共享 Mutex<T> 部分所講的它可以被用來在多線程中共享訪問。

手動(dòng)實(shí)現(xiàn) Send 和 Sync 是不安全的

通常并不需要手動(dòng)實(shí)現(xiàn) Send 和 Sync trait,因?yàn)橛?nbsp;Send 和 Sync 的類型組成的類型,自動(dòng)就是 Send 和 Sync 的。因?yàn)樗麄兪菢?biāo)記 trait,甚至都不需要實(shí)現(xiàn)任何方法。他們只是用來加強(qiáng)并發(fā)相關(guān)的不可變性的。

手動(dòng)實(shí)現(xiàn)這些標(biāo)記 trait 涉及到編寫不安全的 Rust 代碼,第十九章將會(huì)講述具體的方法;當(dāng)前重要的是,在創(chuàng)建新的由不是 Send 和 Sync 的部分構(gòu)成的并發(fā)類型時(shí)需要多加小心,以確保維持其安全保證。“The Rustonomicon” 中有更多關(guān)于這些保證以及如何維持他們的信息。

總結(jié)

這不會(huì)是本書最后一個(gè)出現(xiàn)并發(fā)的章節(jié):第二十章的項(xiàng)目會(huì)在更現(xiàn)實(shí)的場(chǎng)景中使用這些概念,而不像本章中討論的這些小例子。

正如之前提到的,因?yàn)?Rust 本身很少有處理并發(fā)的部分內(nèi)容,有很多的并發(fā)方案都由 crate 實(shí)現(xiàn)。他們比標(biāo)準(zhǔn)庫要發(fā)展的更快;請(qǐng)?jiān)诰W(wǎng)上搜索當(dāng)前最新的用于多線程場(chǎng)景的 crate。

Rust 提供了用于消息傳遞的信道,和像 Mutex<T> 和 Arc<T> 這樣可以安全的用于并發(fā)上下文的智能指針。類型系統(tǒng)和借用檢查器會(huì)確保這些場(chǎng)景中的代碼,不會(huì)出現(xiàn)數(shù)據(jù)競爭和無效的引用。一旦代碼可以編譯了,我們就可以堅(jiān)信這些代碼可以正確的運(yùn)行于多線程環(huán)境,而不會(huì)出現(xiàn)其他語言中經(jīng)常出現(xiàn)的那些難以追蹤的 bug。并發(fā)編程不再是什么可怕的概念:無所畏懼地并發(fā)吧!

接下來,讓我們討論一下當(dāng) Rust 程序變得更大時(shí),有哪些符合語言習(xí)慣的問題建模方法和結(jié)構(gòu)化解決方案,以及 Rust 的風(fēng)格是如何與面向?qū)ο缶幊蹋∣bject Oriented Programming)中那些你所熟悉的概念相聯(lián)系的。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)