Go語(yǔ)言 原子操作


原子操作就是不可中斷的操作,外界是看不到原子操作的中間狀態(tài),要么看到原子操作已經(jīng)完成岸霹,要么看到原子操作已經(jīng)結(jié)束。在某個(gè)值的原子操作執(zhí)行的過(guò)程中将饺,CPU絕對(duì)不會(huì)再去執(zhí)行其他針對(duì)該值的操作贡避,那么其他操作也是原子操作。

Go語(yǔ)言中提供的原子操作都是非侵入式的予弧,在標(biāo)準(zhǔn)庫(kù)代碼包sync/atomic中提供了相關(guān)的原子函數(shù)刮吧。

增或減
用于增或減的原子操作的函數(shù)名稱(chēng)都是以"Add"開(kāi)頭的,后面跟具體的類(lèi)型名掖蛤,比如下面這個(gè)示例就是int64類(lèi)型的原子減操作

func main() {
   var  counter int64 =  23
   atomic.AddInt64(&counter,-3)
   fmt.Println(counter)
}
---output---
20

原子函數(shù)的第一個(gè)參數(shù)都是指向變量類(lèi)型的指針杀捻,是因?yàn)樵硬僮餍枰涝撟兞吭趦?nèi)存中的存放位置,然后加以特殊的CPU指令蚓庭,也就是說(shuō)對(duì)于不能取得內(nèi)存存放地址的變量是無(wú)法進(jìn)行原子操作的致讥。第二個(gè)參數(shù)的類(lèi)型會(huì)自動(dòng)轉(zhuǎn)換為與第一個(gè)參數(shù)相同的類(lèi)型。此外彪置,原子操作會(huì)自動(dòng)將操作后的值賦值給變量拄踪,無(wú)需我們自己手動(dòng)賦值了蝇恶。

對(duì)于 atomic.AddUint32() 和 atomic.AddUint64() 的第二個(gè)參數(shù)為 uint32 與 uint64拳魁,因此無(wú)法直接傳遞一個(gè)負(fù)的數(shù)值進(jìn)行減法操作,Go語(yǔ)言提供了另一種方法來(lái)迂回實(shí)現(xiàn):使用二進(jìn)制補(bǔ)碼的特性

注意:unsafe.Pointer 類(lèi)型的值無(wú)法被加減撮弧。

比較并交換(Compare And Swap)
簡(jiǎn)稱(chēng)CAS潘懊,在標(biāo)準(zhǔn)庫(kù)代碼包sync/atomic中以”Compare And Swap“為前綴的若干函數(shù)就是CAS操作函數(shù)姚糊,比如下面這個(gè)

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)

第一個(gè)參數(shù)的值是這個(gè)變量的指針,第二個(gè)參數(shù)是這個(gè)變量的舊值授舟,第三個(gè)參數(shù)指的是這個(gè)變量的新值救恨。

運(yùn)行過(guò)程:調(diào)用CompareAndSwapInt32 后,會(huì)先判斷這個(gè)指針上的值是否跟舊值相等释树,若相等肠槽,就用新值覆蓋掉這個(gè)值,若相等奢啥,那么后面的操作就會(huì)被忽略掉秸仙。返回一個(gè) swapped 布爾值,表示是否已經(jīng)進(jìn)行了值替換操作桩盲。

與鎖有不同之處:鎖總是假設(shè)會(huì)有并發(fā)操作修改被操作的值寂纪,而CAS總是假設(shè)值沒(méi)有被修改,因此CAS比起鎖要更低的性能損耗赌结,鎖被稱(chēng)為悲觀鎖捞蛋,而CAS被稱(chēng)為樂(lè)觀鎖。

CAS的使用示例

var value int32
func AddValue(delta int32)  {
   for {
      v:= value
      if atomic.CompareAndSwapInt32(&value,v,(v+delta)) {
         break
      }
   }
}

由示例可以看出柬姚,我們需要多次使用for循環(huán)來(lái)判斷該值是否已被更改拟杉,為了保證CAS操作成功,僅在 CompareAndSwapInt32 返回為 true時(shí)才退出循環(huán)量承,這跟自旋鎖的自旋行為相似捣域。

載入與存儲(chǔ)
對(duì)一個(gè)值進(jìn)行讀或?qū)憰r(shí),并不代表這個(gè)值是最新的值宴合,也有可能是在在讀或?qū)懙倪^(guò)程中進(jìn)行了并發(fā)的寫(xiě)操作導(dǎo)致原值改變焕梅。為了解決這問(wèn)題,Go語(yǔ)言的標(biāo)準(zhǔn)庫(kù)代碼包sync/atomic提供了原子的讀蓉郧ⅰ(Load為前綴的函數(shù))或?qū)懭耄⊿tore為前綴的函數(shù))某個(gè)值

將上面的示例改為原子讀取

var value int32
func AddValue(delta int32)  {
   for {
      v:= atomic.LoadInt32(&value)
      if atomic.CompareAndSwapInt32(&value,v,(v+delta)) {
         break
      }
   }
}

原子寫(xiě)入總會(huì)成功贞言,因?yàn)樗恍枰P(guān)心原值是什么,而CAS中必須關(guān)注舊值阀蒂,因此原子寫(xiě)入并不能代替CAS该窗,原子寫(xiě)入包含兩個(gè)參數(shù),以下面的StroeInt32為例:

//第一個(gè)參數(shù)是被操作值的指針蚤霞,第二個(gè)是被操作值的新值
func StoreInt32(addr *int32, val int32) 

交換
這類(lèi)操作都以”Swap“開(kāi)頭的函數(shù)酗失,稱(chēng)為”原子交換操作“,功能與之前說(shuō)的CAS操作與原子寫(xiě)入操作有相似之處昧绣。

func SwapInt32(addr *int32, new int32) (old int32)

以 SwapInt32 為例规肴,第一個(gè)參數(shù)是int32類(lèi)型的指針,第二個(gè)是新值。原子交換操作不需要關(guān)心原值拖刃,而是直接設(shè)置新值删壮,但是會(huì)返回被操作值的舊值。

原子值
Go語(yǔ)言的標(biāo)準(zhǔn)庫(kù)代碼包sync/atomic中有一個(gè)叫做Value的原子值兑牡,它是一個(gè)結(jié)構(gòu)體類(lèi)型央碟,用于存儲(chǔ)需要原子讀寫(xiě)的值,結(jié)構(gòu)體如下

// Value提供原子加載并存儲(chǔ)一致類(lèi)型的值均函。
// Value的零值從Load返回nil亿虽。
//調(diào)用Store后,不得復(fù)制值苞也。
//首次使用后不得復(fù)制值经柴。
type Value struct {
   v interface{}
}

可以看出結(jié)構(gòu)體內(nèi)是一個(gè) v interface{},也就是說(shuō) 該Value原子值可以保存任何類(lèi)型的需要原子讀寫(xiě)的值墩朦。

使用方式如下:

var Atomicvalue  atomic.Value

該類(lèi)型有兩個(gè)公開(kāi)的指針?lè)椒?/p>

//原子的讀取原子值實(shí)例中存儲(chǔ)的值坯认,返回一個(gè) interface{} 類(lèi)型的值,且不接受任何參數(shù)氓涣。
//若未曾通過(guò)store方法存儲(chǔ)值之前牛哺,會(huì)返回nil
func (v *Value) Load() (x interface{})

//原子的在原子實(shí)例中存儲(chǔ)一個(gè)值,接收一個(gè) interface{} 類(lèi)型(不能為nil)的參數(shù)劳吠,且不會(huì)返回任何值
func (v *Value) Store(x interface{})

一旦原子值實(shí)例存儲(chǔ)了某個(gè)類(lèi)型的值引润,那么之后Store存儲(chǔ)的值就必須是與該類(lèi)型一致,否則就會(huì)引發(fā)panic痒玩。

嚴(yán)格來(lái)講淳附,atomic.Value類(lèi)型的變量一旦被聲明,就不應(yīng)該被復(fù)制到其他地方蠢古。比如:作為源值賦值給其他變量奴曙,作為參數(shù)傳遞給函數(shù),作為結(jié)果值從函數(shù)返回草讶,作為元素值通過(guò)通道傳遞洽糟,這些都會(huì)造成值的復(fù)制。

但是atomic.Value類(lèi)型的指針類(lèi)型變量就不會(huì)存在這個(gè)問(wèn)題堕战,原因是對(duì)結(jié)構(gòu)體的復(fù)制不但會(huì)生成該值的副本坤溃,還會(huì)生成其中字段的副本,這樣那么并發(fā)引發(fā)的值變化都與原值沒(méi)關(guān)系了嘱丢。

看下面這個(gè)小示例

func main() {
   var Atomicvalue  atomic.Value
   Atomicvalue.Store([]int{1,2,3,4,5})
   anotherStore(Atomicvalue)
   fmt.Println("main: ",Atomicvalue)
}

func anotherStore(Atomicvalue atomic.Value)  {
   Atomicvalue.Store([]int{6,7,8,9,10})
   fmt.Println("anotherStore: ",Atomicvalue)
}
---output---
anotherStore:  {[6 7 8 9 10]}
main:  {[1 2 3 4 5]}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末薪介,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子越驻,更是在濱河造成了極大的恐慌汁政,老刑警劉巖道偷,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異烂完,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诵棵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)抠蚣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人履澳,你說(shuō)我怎么就攤上這事嘶窄。” “怎么了距贷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵柄冲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我忠蝗,道長(zhǎng)现横,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任阁最,我火速辦了婚禮戒祠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘速种。我一直安慰自己姜盈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布配阵。 她就那樣靜靜地躺著馏颂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棋傍。 梳的紋絲不亂的頭發(fā)上救拉,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音瘫拣,去河邊找鬼近上。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拂铡,可吹牛的內(nèi)容都是我干的壹无。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼感帅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼斗锭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起失球,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岖是,失蹤者是張志新(化名)和其女友劉穎帮毁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體豺撑,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烈疚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了聪轿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爷肝。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陆错,靈堂內(nèi)的尸體忽然破棺而出灯抛,到底是詐尸還是另有隱情,我是刑警寧澤音瓷,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布对嚼,位于F島的核電站,受9級(jí)特大地震影響绳慎,放射性物質(zhì)發(fā)生泄漏纵竖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一杏愤、第九天 我趴在偏房一處隱蔽的房頂上張望磨确。 院中可真熱鬧,春花似錦声邦、人聲如沸乏奥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)邓了。三九已至,卻和暖如春媳瞪,著一層夾襖步出監(jiān)牢的瞬間骗炉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工蛇受, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留句葵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓兢仰,卻偏偏與公主長(zhǎng)得像乍丈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子把将,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容