網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Rust?Atomics?and?Locks內(nèi)存序Memory?Ordering詳解_Rust語(yǔ)言
作者:mikko7331 ? 更新時(shí)間: 2023-06-21 編程語(yǔ)言Rust內(nèi)存序
Memory Ordering規(guī)定了多線程環(huán)境下對(duì)共享內(nèi)存進(jìn)行操作時(shí)的可見(jiàn)性和順序性,防止了不正確的重排序(Reordering)。
重排序和優(yōu)化
重排序是指編譯器或CPU在不改變程序語(yǔ)義的前提下,改變指令的執(zhí)行順序。在單線程環(huán)境下,重排序可能會(huì)帶來(lái)性能提升,但在多線程環(huán)境下,重排序可能會(huì)破壞程序的正確性,導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)、死鎖等問(wèn)題。
Rust提供了多種內(nèi)存序,包括Acquire、Release、AcqRel、SeqCst等。這些內(nèi)存序規(guī)定了在不同情況下,線程之間進(jìn)行共享內(nèi)存的讀寫時(shí)應(yīng)該保持的順序和可見(jiàn)性。
除了內(nèi)存序之外,編譯器還可以進(jìn)行優(yōu)化,例如常數(shù)折疊、函數(shù)內(nèi)聯(lián)等。這些優(yōu)化可能會(huì)導(dǎo)致指令重排,從而影響多線程程序的正確性。為了避免這種情況,Rust提供了關(guān)鍵字volatile
和compiler_fence
來(lái)禁止編譯器進(jìn)行優(yōu)化,保證程序的正確性。
總的來(lái)說(shuō),Rust的內(nèi)存序機(jī)制和優(yōu)化控制機(jī)制可以幫助程序員在多線程環(huán)境下編寫高效且正確的程序。 下面是一個(gè)簡(jiǎn)單的 Rust 代碼示例,它演示了 Rust 中的代碼重新排序和優(yōu)化如何影響程序行為:
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let shared_counter = Arc::new(Mutex::new(0)); let mut threads = vec![]; for i in 0..10 { let counter = shared_counter.clone(); let t = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += i; }); threads.push(t); } for t in threads { t.join().unwrap(); } let final_counter = shared_counter.lock().unwrap(); println!("Final value: {}", *final_counter); }
在這個(gè)示例中,我們創(chuàng)建了一個(gè)共享計(jì)數(shù)器 shared_counter
,它被多個(gè)線程并發(fā)地訪問(wèn)和修改。為了保證線程安全,我們使用了一個(gè) Mutex
來(lái)對(duì)計(jì)數(shù)器進(jìn)行互斥訪問(wèn)。
在主線程中,我們創(chuàng)建了 10 個(gè)子線程,并讓它們分別增加計(jì)數(shù)器的值。然后我們等待所有線程都執(zhí)行完畢后,打印出最終的計(jì)數(shù)器值。
在這個(gè)示例中,由于 Rust 的內(nèi)存序保證,所有對(duì)共享變量的訪問(wèn)和修改都按照程序中的順序進(jìn)行。也就是說(shuō),每個(gè)線程增加計(jì)數(shù)器的值的操作不會(huì)重排到其他線程之前或之后的位置。
不過(guò),如果我們?cè)诖a中加入一些優(yōu)化指令,就可能會(huì)破壞這種順序。比如,下面這段代碼就使用了 fence
指令來(lái)保證所有線程對(duì)共享變量的修改都在主線程中得到了同步:
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let shared_counter = Arc::new(Mutex::new(0)); let mut threads = vec![]; for i in 0..10 { let counter = shared_counter.clone(); let t = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += i; std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); }); threads.push(t); } for t in threads { t.join().unwrap(); } let final_counter = shared_counter.lock().unwrap(); println!("Final value: {}", *final_counter); }
在這個(gè)示例中,我們?cè)诿總€(gè)子線程結(jié)束時(shí)都加入了一個(gè) fence
指令,來(lái)確保所有線程對(duì)共享變量的修改都在主線程中得到了同步。這樣一來(lái),雖然所有線程對(duì)計(jì)數(shù)器的修改仍然是并發(fā)進(jìn)行的,但它們對(duì)計(jì)數(shù)器的修改操作的順序可能會(huì)被重新排序,從而導(dǎo)致最終的計(jì)數(shù)器值與期望值不同。 但是,這可能是由于編譯器的優(yōu)化策略和硬件平臺(tái)的差異所導(dǎo)致的。在某些情況下,編譯器可能會(huì)選擇不進(jìn)行代碼重排或重新優(yōu)化,因?yàn)檫@可能會(huì)影響程序的正確性。但是,在其他情況下,編譯器可能會(huì)根據(jù)其優(yōu)化策略和目標(biāo)平臺(tái)的特性來(lái)對(duì)代碼進(jìn)行重排和重新優(yōu)化,這可能會(huì)導(dǎo)致程序的行為發(fā)生變化。
happens-before
Rust內(nèi)存模型中的“happens-before”原則指的是,如果一個(gè)操作A happens before 另一個(gè)操作B,那么A在時(shí)間上先于B執(zhí)行,而且A對(duì)內(nèi)存的影響對(duì)于B是可見(jiàn)的。這個(gè)原則被用來(lái)解決多線程環(huán)境下的數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,確保程序的執(zhí)行順序是有序的,避免出現(xiàn)未定義的行為。
具體來(lái)說(shuō),Rust內(nèi)存模型中的happens-before原則包括以下幾個(gè)方面:
- 內(nèi)存同步操作:Rust的內(nèi)存同步操作(如acquire、release、acqrel、seq_cst)會(huì)創(chuàng)建一個(gè)happens-before的關(guān)系,保證該操作之前的所有內(nèi)存訪問(wèn)對(duì)該操作之后的內(nèi)存訪問(wèn)都是可見(jiàn)的。
- 鎖機(jī)制:Rust的鎖機(jī)制(如Mutex、RwLock)也會(huì)創(chuàng)建happens-before的關(guān)系,保證鎖內(nèi)的操作是有序的,避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。
- 線程的啟動(dòng)和結(jié)束:Rust的線程啟動(dòng)函數(shù)(如thread::spawn)會(huì)創(chuàng)建happens-before的關(guān)系,保證線程啟動(dòng)之前的所有內(nèi)存訪問(wèn)對(duì)于該線程中的所有操作都是可見(jiàn)的。線程結(jié)束時(shí)也會(huì)創(chuàng)建happens-before的關(guān)系,保證該線程中的所有操作對(duì)于其他線程都是可見(jiàn)的。
- Atomics:Rust的原子類型(如AtomicBool、AtomicUsize)也會(huì)創(chuàng)建happens-before的關(guān)系,保證對(duì)于同一個(gè)原子變量的多次操作是有序的,避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。
總之,Rust內(nèi)存模型中的happens-before原則確保程序的執(zhí)行順序是有序的,避免出現(xiàn)未定義的行為,從而幫助開(kāi)發(fā)者避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。
Relexed Ordering
在 Rust 中,Relaxed Ordering 是一種較弱的內(nèi)存順序,它允許線程在不同于程序中寫入順序的順序中讀取或?qū)懭霐?shù)據(jù),但不會(huì)導(dǎo)致未定義的行為。
Relaxed Ordering 主要應(yīng)用于不需要同步的操作,比如單線程的計(jì)數(shù)器、讀取全局配置等場(chǎng)景。使用 Relaxed Ordering 可以避免不必要的內(nèi)存屏障,提高程序的性能。
在 Rust 中,可以通過(guò) std::sync::atomic::AtomicXXX
類型來(lái)使用 Relaxed Ordering。比如下面這個(gè)示例:
use std::sync::atomic::{AtomicUsize, Ordering}; fn main() { let counter = AtomicUsize::new(0); counter.fetch_add(1, Ordering::Relaxed); let value = counter.load(Ordering::Relaxed); println!("counter: {}", value); }
在這個(gè)示例中,我們使用 AtomicUsize
類型創(chuàng)建了一個(gè)計(jì)數(shù)器 counter
,然后使用 fetch_add
方法對(duì)計(jì)數(shù)器進(jìn)行自增操作,并使用 load
方法讀取當(dāng)前計(jì)數(shù)器的值。在 fetch_add
和 load
方法中,我們使用了 Ordering::Relaxed
參數(shù),表示這是一個(gè) Relaxed Ordering 的操作,不需要執(zhí)行額外的內(nèi)存屏障。
需要注意的是,使用 Relaxed Ordering 時(shí)需要保證程序中不存在數(shù)據(jù)競(jìng)爭(zhēng)。如果存在數(shù)據(jù)競(jìng)爭(zhēng),就可能會(huì)導(dǎo)致內(nèi)存重排和未定義的行為。因此,建議僅在確信不會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)的情況下使用 Relaxed Ordering。
Release 和 Acquire Ordering
在 Rust 中,Release 和 Acquire Ordering 通常用于實(shí)現(xiàn)同步原語(yǔ),例如 Mutex 和 Atomic 原子類型,以確保線程之間的正確同步。
Acquire Ordering 表示一個(gè)讀取操作所需的同步操作。在讀取操作之前,必須確保任何在之前的寫入操作都已經(jīng)完成,并且這些寫入操作對(duì)其他線程可見(jiàn)。在 Acquire Ordering 中,讀取操作前的任何寫入操作都不能被重排序到讀取操作之后。
Release Ordering 表示一個(gè)寫入操作所需的同步操作。在寫入操作之后,必須確保任何在之后的讀取操作都能夠看到這個(gè)寫入操作的結(jié)果,并且這個(gè)寫入操作對(duì)其他線程可見(jiàn)。在 Release Ordering 中,寫入操作后的任何讀取操作都不能被重排序到寫入操作之前。
下面是一個(gè)簡(jiǎn)單的示例,說(shuō)明了如何使用 Release 和 Acquire Ordering 來(lái)同步多個(gè)線程對(duì)共享狀態(tài)的訪問(wèn):
use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; static SHARED_STATE: AtomicUsize = AtomicUsize::new(0); fn main() { let mut threads = Vec::new(); for i in 0..5 { let thread = thread::spawn(move || { let mut local_state = i; // 等待其他線程初始化 thread::sleep_ms(10); // 將本地狀態(tài)更新到共享狀態(tài)中 SHARED_STATE.store(local_state, Ordering::Release); // 讀取共享狀態(tài)中的值 let shared_state = SHARED_STATE.load(Ordering::Acquire); println!("Thread {}: Shared state = {}", i, shared_state); }); threads.push(thread); } for thread in threads { thread.join().unwrap(); } }
在這個(gè)例子中,五個(gè)線程將本地狀態(tài)更新到共享狀態(tài)中,并讀取其他線程更新的共享狀態(tài)。為了確保正確的同步,我們使用 Release Ordering 來(lái)保證寫入操作的同步,Acquire Ordering 來(lái)保證讀取操作的同步。在每個(gè)線程中,我們使用 thread::sleep_ms(10)
使所有線程都能夠開(kāi)始執(zhí)行,并等待其他線程完成初始化。在主線程中,我們使用 join
等待所有線程完成。運(yùn)行此程序,輸出可能類似于以下內(nèi)容:
Thread 0: Shared state = 3
Thread 1: Shared state = 0
Thread 2: Shared state = 2
Thread 3: Shared state = 1
Thread 4: Shared state = 4
這表明每個(gè)線程都能夠正確地讀取其他線程更新的共享狀態(tài),并且寫入操作在讀取操作之前完成。
SeqCst Ordering
SeqCst是Rust內(nèi)存模型中的一種內(nèi)存順序,它保證了所有的操作都按照順序執(zhí)行。SeqCst可以用于實(shí)現(xiàn)最嚴(yán)格的同步,因?yàn)樗_保了所有線程都看到相同的執(zhí)行順序,因此被廣泛用于實(shí)現(xiàn)同步原語(yǔ)。 如果無(wú)法確認(rèn)使用哪種排序的話,可以直接使用SeqCst
使用SeqCst內(nèi)存順序時(shí),讀操作和寫操作的執(zhí)行順序都是全局可見(jiàn)的,因此可以避免數(shù)據(jù)競(jìng)爭(zhēng)和其他問(wèn)題。但是SeqCst內(nèi)存順序會(huì)導(dǎo)致一些性能問(wèn)題,因?yàn)樗笏芯€程都同步執(zhí)行,這可能會(huì)導(dǎo)致一些線程被阻塞。
以下是一個(gè)簡(jiǎn)單的示例,展示了如何在Rust中使用SeqCst內(nèi)存順序:
use std::sync::atomic::{AtomicBool, Ordering}; fn main() { let val = AtomicBool::new(false); val.store(true, Ordering::SeqCst); let result = val.load(Ordering::SeqCst); println!("Result: {}", result); }
在這個(gè)示例中,我們創(chuàng)建了一個(gè)AtomicBool
類型的變量val
,并將其初始值設(shè)置為false
。然后,我們使用store
方法將其值設(shè)置為true
,并使用load
方法讀取它的值。在這里,我們使用了SeqCst
內(nèi)存順序,以確保所有線程都按順序執(zhí)行。在這個(gè)例子中,程序會(huì)輸出Result: true
,因?yàn)槲覀兪褂昧?code>SeqCst內(nèi)存順序,保證了所有線程都看到相同的執(zhí)行順序。
原文鏈接:https://juejin.cn/post/7204115174411845691
- 上一篇:沒(méi)有了
- 下一篇:沒(méi)有了
相關(guān)推薦
- 2022-06-13 詳解Python+Matplotlib繪制面積圖&熱力圖_python
- 2022-04-17 spring cloud config和bus組件實(shí)現(xiàn)自動(dòng)刷新
- 2022-10-14 MapStruct 代碼生成器
- 2022-09-16 Pandas統(tǒng)計(jì)計(jì)數(shù)value_counts()的使用_python
- 2022-06-18 C語(yǔ)言簡(jiǎn)明講解單引號(hào)與雙引號(hào)的使用_C 語(yǔ)言
- 2022-10-16 python正則表達(dá)式re.group()用法_python
- 2022-06-09 FreeRTOS實(shí)時(shí)操作系統(tǒng)空閑任務(wù)的阻塞延時(shí)實(shí)現(xiàn)_操作系統(tǒng)
- 2023-01-31 React受控組件與非受控組件深入講解_React
- 欄目分類
-
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支