并發(fā)編程 - 線程同步(三)之原子操作Interlocked簡介
上一章我們了解了3種處理多線程中共享資源安全的方法,今天我們將更近一步,學(xué)習(xí)一種針對簡單線程同步場景的解決方案——Interlocked。

在此之前我們先學(xué)習(xí)一個(gè)概念——原子操作。
01、原子操作
原子操作,其概念源于化學(xué)領(lǐng)域,原子是構(gòu)成化學(xué)元素的普通物質(zhì)的最小單位;原子也是化學(xué)變化中最小的粒子及元素化學(xué)性質(zhì)的最小單位。
借鑒到編程語言中原子操作指:不可分割的操作單元,是指一類不可中斷的操作,它在執(zhí)行時(shí)要么全部執(zhí)行,要么全部不執(zhí)行,不會(huì)被其他操作打斷,而執(zhí)行結(jié)果要么全部成功,要不全部失敗,沒有其他狀態(tài)。
我們再回憶一下我們學(xué)習(xí)線程同步的目的——確保多個(gè)線程在訪問共享資源時(shí)能夠按順序、安全地進(jìn)行操作,從而避免并發(fā)執(zhí)行帶來的數(shù)據(jù)競爭和不一致的狀態(tài)。
可以說原子操作天然的解決了多線程共享資源安全問題。
在C#語言中,Interlocked類提供了一系列可以進(jìn)行原子操作的工具。
02、Interlocked實(shí)現(xiàn)原理
Interlocked的原子操作是基于CPU本身實(shí)現(xiàn),是硬件級別的原子指令封裝,并且它不需要顯式的線程阻塞,因此比傳統(tǒng)的鎖機(jī)制(如互斥鎖、信號量等)效率更高,尤其是在高并發(fā)的場景下。
線程阻塞是指操作系統(tǒng)把當(dāng)前線程從運(yùn)行狀態(tài)變更為阻塞狀態(tài),并且操作系統(tǒng)會(huì)把當(dāng)前線程占用的CPU時(shí)間片分配給其他線程,使當(dāng)前線程盡可能少的占用CPU時(shí)間。在分配CPU時(shí)間片時(shí)會(huì)涉及上下文切換等操作。
因此Interlocked的非阻塞特性,嚴(yán)格意義上來說并不是鎖,但是效率卻比鎖高得多。
另外如果一個(gè)方法在CPU層面上被設(shè)計(jì)為對立不可分割的指令,那么它本質(zhì)上就是原子的,嚴(yán)格的原子性可以阻止任何搶占的可能。因此在32位CPU中,一個(gè)操作數(shù)的大小為32位,因此可以提供32位即4個(gè)字節(jié)大小及以內(nèi)的數(shù)據(jù)類型(如int,float)進(jìn)行讀寫的原子操作。同理64位CPU,則可以提供64位級8個(gè)字節(jié)大小及以內(nèi)的數(shù)據(jù)類型(如long,double)進(jìn)行讀寫的原子操作。
其實(shí)在上一章也舉個(gè)一個(gè)例子,在32位CPU環(huán)境下操作long類型,多線程情況下會(huì)出現(xiàn)線程不完全問題,因?yàn)閷τ?2位CPU,操作一次long類型數(shù)據(jù)至少需要兩個(gè)原子指令,因此就會(huì)出現(xiàn)線程安全問題。
03、Interlocked常用方法
Interlocked方法從.NET Framework 1.1到目前最新的.NET 9也經(jīng)歷了長足的發(fā)展和完善。可以總結(jié)為:支持的操作類型在增加,操作能力也再增加,由早期的簡單遞增、遞減、替換操作到現(xiàn)在的復(fù)雜位操作,內(nèi)存屏障操作等。
下面我們先整體了解一下Interlocked有那些方法。

-
Read: 原子的讀取64位值;
-
Increment: 原子的遞增指定的變量,并返回遞增后的新值;
-
Decrement: 原子的遞減指定的變量,并返回遞減后的新值;
-
Add: 原子的對兩個(gè)變量求和,將第一個(gè)變量替換為兩者和,并返回操作后第一個(gè)變量的新值;
-
Exchange: 原子的交換兩個(gè)變量,并返回第一個(gè)變量的原始值;
-
Exchange
: Exchange方法的泛型版本; -
CompareExchange: 原子的比較第一個(gè)變量和第三個(gè)變量是否相等,如果相等,則將第一個(gè)變量替換為第二個(gè)變量值,并返回第一個(gè)變量的原始值;
-
CompareExchange
: CompareExchange方法的泛型版本; -
And: 原子的對兩個(gè)變量進(jìn)行按位與操作,將第一個(gè)變量替換為操作結(jié)果,并返回第一個(gè)變量的原始值;
-
Or: 原子的對兩個(gè)變量進(jìn)行按位或操作,將第一個(gè)變量替換為操作結(jié)果,并返回第一個(gè)變量的原始值;
-
MemoryBarrier: 強(qiáng)制執(zhí)行內(nèi)存屏障,作用范圍當(dāng)前線程,無返回值;
-
MemoryBarrierProcessWide: 提供進(jìn)程范圍的內(nèi)存屏障,確保任何 CPU 的讀取和寫入無法跨屏障移動(dòng);
后面我們將詳細(xì)講解每個(gè)方法的如何使用。
注:測試方法代碼以及示例源碼都已經(jīng)上傳至代碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner

浙公網(wǎng)安備 33010602011771號