欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Rust編程中的共享狀態(tài)并發(fā)執(zhí)行

 更新時間:2023年11月15日 10:40:41   作者:二進(jìn)制空間安全  
雖然消息傳遞是一個很好的處理并發(fā)的方式,但并不是唯一一個,另一種方式是讓多個線程擁有相同的共享數(shù)據(jù),本文給大家介紹Rust編程中的共享狀態(tài)并發(fā)執(zhí)行,感興趣的朋友一起看看吧

1.共享狀態(tài)并發(fā)

雖然消息傳遞是一個很好的處理并發(fā)的方式,但并不是唯一一個。另一種方式是讓多個線程擁有相同的共享數(shù)據(jù)。在學(xué)習(xí)Go語言編程過程中大家應(yīng)該聽到過一句口號:"不要通過共享內(nèi)存來通訊"。

在某種程度上,任何編程語言中的信道都類似于單所有權(quán),因為一旦將一個值傳送到信道中,將無法再使用這個值。共享內(nèi)存類似于多所有權(quán):多個線程可以同時訪問相同的內(nèi)存位置。第十五章介紹了智能指針如何使得多所有權(quán)成為可能,然而這會增加額外的復(fù)雜性,因為需要以某種方式管理這些不同的所有者。Rust 的類型系統(tǒng)和所有權(quán)規(guī)則極大的協(xié)助了正確地管理這些所有權(quán)。作為一個例子,讓我們看看互斥器,一個更為常見的共享內(nèi)存并發(fā)原語。

互斥器(mutex)是 mutual exclusion 的縮寫,也就是說,任意時刻,其只允許一個線程訪問某些數(shù)據(jù)。為了訪問互斥器中的數(shù)據(jù),線程首先需要通過獲取互斥器的 (lock)來表明其希望訪問數(shù)據(jù)。鎖是一個作為互斥器一部分的數(shù)據(jù)結(jié)構(gòu),它記錄誰有數(shù)據(jù)的排他訪問權(quán)。因此,我們描述互斥器為通過鎖系統(tǒng) 保護(guarding)其數(shù)據(jù)。

互斥器以難以使用著稱,因為你不得不記住:

  • 在使用數(shù)據(jù)之前嘗試獲取鎖。
  • 處理完被互斥器所保護的數(shù)據(jù)之后,必須解鎖數(shù)據(jù),這樣其他線程才能夠獲取鎖。

作為一個現(xiàn)實中互斥器的例子,想象一下在某個會議的一次小組座談會中,只有一個麥克風(fēng)。如果一位成員要發(fā)言,他必須請求或表示希望使用麥克風(fēng)。一旦得到了麥克風(fēng),他可以暢所欲言,然后將麥克風(fēng)交給下一位希望講話的成員。如果一位成員結(jié)束發(fā)言后忘記將麥克風(fēng)交還,其他人將無法發(fā)言。如果對共享麥克風(fēng)的管理出現(xiàn)了問題,座談會將無法如期進(jìn)行!

正確的管理互斥器異常復(fù)雜,這也是許多人之所以熱衷于信道的原因。然而,在 Rust 中,得益于類型系統(tǒng)和所有權(quán),我們不會在鎖和解鎖上出錯。

2.Mutex<T>的API

作為展示如何使用互斥器的例子,讓我們從在單線程上下文使用互斥器開始, 看下面的代碼:

use std::sync::Mutex;
fn main() {
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }
    println!("m = {:?}", m);
}

像很多類型一樣,我們使用關(guān)聯(lián)函數(shù) new 來創(chuàng)建一個 Mutex<T>。使用 lock 方法獲取鎖,以訪問互斥器中的數(shù)據(jù)。這個調(diào)用會阻塞當(dāng)前線程,直到我們擁有鎖為止。

如果另一個線程擁有鎖,并且那個線程 panic 了,則 lock 調(diào)用會失敗。在這種情況下,沒人能夠再獲取鎖,所以這里選擇 unwrap 并在遇到這種情況時使線程 panic。

一旦獲取了鎖,就可以將返回值(在這里是num)視為一個其內(nèi)部數(shù)據(jù)的可變引用了。類型系統(tǒng)確保了我們在使用 m 中的值之前獲取鎖。m 的類型是 Mutex<i32> 而不是 i32,所以 必須 獲取鎖才能使用這個 i32 值。我們是不會忘記這么做的,因為反之類型系統(tǒng)不允許訪問內(nèi)部的 i32 值。

Mutex<T> 是一個智能指針。更準(zhǔn)確的說,lock 調(diào)用 返回 一個叫做 MutexGuard 的智能指針。這個智能指針實現(xiàn)了 Deref 來指向其內(nèi)部數(shù)據(jù);其也提供了一個 Drop 實現(xiàn)當(dāng) MutexGuard 離開作用域時自動釋放鎖,為此,我們不會忘記釋放鎖并阻塞互斥器為其它線程所用的風(fēng)險,因為鎖的釋放是自動發(fā)生的。

丟棄了鎖之后,可以打印出互斥器的值,并發(fā)現(xiàn)能夠?qū)⑵鋬?nèi)部的 i32 改為 6。

3.在線程間共享Mutex<T>

現(xiàn)在讓我們嘗試使用 Mutex<T> 在多個線程間共享值。我們將啟動十個線程,并在各個線程中對同一個計數(shù)器值加一,這樣計數(shù)器將從 0 變?yōu)?10??聪旅娴拇a:

use std::sync::Mutex;
use std::thread;
fn main() {
    let counter = Mutex::new(0);
    let mut handles = vec![];
    for _ in 0..10 {
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}

這里創(chuàng)建了一個 counter 變量來存放內(nèi)含 i32Mutex<T>, 接下來遍歷 range 創(chuàng)建了 10 個線程。使用了 thread::spawn 并對所有線程使用了相同的閉包:它們每一個都將調(diào)用 lock 方法來獲取 Mutex<T> 上的鎖,接著將互斥器中的值加一。當(dāng)一個線程結(jié)束執(zhí)行,num 會離開閉包作用域并釋放鎖,這樣另一個線程就可以獲取它了。

在主線程中,我們收集了所有的 join 句柄, 調(diào)用它們的 join 方法來確保所有線程都會結(jié)束。這時,主線程會獲取鎖并打印出程序的結(jié)果。

編譯上面的代碼, Rust編譯器報了一個錯誤:

錯誤信息表明 counter 值在上一次循環(huán)中被移動了。所以 Rust 告訴我們不能將 counter 鎖的所有權(quán)移動到多個線程中。下面來看看如何修復(fù)這個錯誤。

4.多線程和多所有權(quán)

我們先嘗試將Mutex<T>封裝進(jìn)Rc<T>中并在將所有權(quán)移入線程之前克隆Rc<T>,看下面代碼:

use std::rc::Rc;
use std::sync::Mutex;
use std::thread;
fn main() {
    let counter = Rc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let counter = Rc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}

再一次編譯代碼,納尼, 居然又報了另一個錯誤, 成年人的崩潰誰能懂:

Rc<Mutex<i32>>` cannot be sent between threads safely`。這個錯誤編譯器告訴我們原因是:`the trait `Send` is not implemented for `Rc<Mutex<i32>>。

Rc<T> 并不能安全的在線程間共享。當(dāng) Rc<T> 管理引用計數(shù)時,它必須在每一個 clone 調(diào)用時增加計數(shù),并在每一個克隆被丟棄時減少計數(shù)。Rc<T> 并沒有使用任何并發(fā)原語,來確保改變計數(shù)的操作不會被其他線程打斷。在計數(shù)出錯時可能會導(dǎo)致詭異的 bug,比如可能會造成內(nèi)存泄漏,或在使用結(jié)束之前就丟棄一個值。我們所需要的是一個完全類似 Rc<T>,又以一種線程安全的方式改變引用計數(shù)的類型。

5.原子引用計數(shù)Arc<T>

在Rust標(biāo)準(zhǔn)庫中, 提供了一個名為Arc<T>的類型, 這是一個可以安全的用于并發(fā)環(huán)境的類型, 字母 “a” 代表 原子性(atomic),所以這是一個 原子引用計數(shù)(atomically reference counted)類型, 將代碼修改為:

use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}

再次編譯代碼, 執(zhí)行結(jié)果如下:

這次終于得到結(jié)果10, 程序從0數(shù)到10, 雖然過程看上去并不明顯, 但我們卻學(xué)到了很多關(guān)于Mutex<T>和線程安全的內(nèi)容。

到此這篇關(guān)于Rust編程中的共享狀態(tài)并發(fā)執(zhí)行的文章就介紹到這了,更多相關(guān)Rust共享狀態(tài)并發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • rust聲明式宏的實現(xiàn)

    rust聲明式宏的實現(xiàn)

    聲明式宏使得你能夠?qū)懗鲱愃?match?表達(dá)式的東西,來操作你所提供的?Rust代碼,它使用你提供的代碼來生成用于替換宏調(diào)用的代碼,感興趣的可以了解一下
    2023-12-12
  • rust 一個日志緩存記錄的通用實現(xiàn)方法

    rust 一個日志緩存記錄的通用實現(xiàn)方法

    本文給出了一個通用的設(shè)計模式,通過建造者模式實例化記錄對象,可自定義格式化器將實例化后的記錄對象寫入到指定的緩存對象中,這篇文章主要介紹了rust 一個日志緩存記錄的通用實現(xiàn)方法,需要的朋友可以參考下
    2024-04-04
  • rust中async/await的使用示例詳解

    rust中async/await的使用示例詳解

    在Rust中,async/await用于編寫異步代碼,使得異步操作更易于理解和編寫,通過使用await,在async函數(shù)或代碼塊中等待Future完成,而不會阻塞線程,允許同時執(zhí)行其他Future,這種機制簡化了異步編程的復(fù)雜性,使代碼更加直觀
    2024-10-10
  • rust文件讀寫的實現(xiàn)示例

    rust文件讀寫的實現(xiàn)示例

    Rust語言提供了強大的文件讀寫庫,使得開發(fā)者可以更加方便地進(jìn)行文件操作,并且其安全性可以有效避免文件操作中可能出現(xiàn)的風(fēng)險,本文就來詳細(xì)的介紹了rust文件讀寫的實現(xiàn)示例,感興趣的可以了解一下
    2023-12-12
  • 利用Rust實現(xiàn)一個簡單的Ping應(yīng)用

    利用Rust實現(xiàn)一個簡單的Ping應(yīng)用

    這兩年Rust火的一塌糊涂,甚至都燒到了前端,再不學(xué)習(xí)怕是要落伍了。最近翻了翻文檔,寫了個簡單的Ping應(yīng)用練練手,感興趣的小伙伴可以了解一下
    2022-12-12
  • Rust包和Crate超詳細(xì)講解

    Rust包和Crate超詳細(xì)講解

    這篇文章主要介紹了Rust包管理和Crate,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-12-12
  • Rust 能夠取代 C 語言嗎

    Rust 能夠取代 C 語言嗎

    Rust 是 Mozilla 基金會的一個雄心勃勃的項目,號稱是 C 語言和 C++ 的繼任者,這篇文章主要介紹了Rust 能夠取代 C 語言嗎的相關(guān)知識,需要的朋友可以參考下
    2020-06-06
  • Rust中字符串類型&str和String的使用

    Rust中字符串類型&str和String的使用

    在Rust中,字符串是一種非常重要的數(shù)據(jù)類型,&str和String是Rust中兩種主要的字符串類型,本文主要介紹了Rust中字符串類型&str和String的使用,感興趣的可以了解一下
    2024-03-03
  • Rust利用tauri制作個效率小工具

    Rust利用tauri制作個效率小工具

    日常使用電腦中經(jīng)常會用到一個quicke工具中的輪盤菜單工具。但quicke免費版很多功能不支持,且它的觸發(fā)邏輯用的不舒服,經(jīng)常誤觸。所以本文就來用tauri自制一個小工具,希望對大家有所幫助
    2023-02-02
  • rust流程控制的具體使用

    rust流程控制的具體使用

    在Rust中,控制流包括條件語句、循環(huán)和匹配模式等,用于實現(xiàn)程序的邏輯和流程控制,本文就來詳細(xì)的介紹一下,感興趣的可以了解一下
    2023-12-12

最新評論