參數(shù)設(shè)計(jì)
func SwapInt32(addr *int32, new int32) (old int32)
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func LoadInt32(addr *int32) (val int32)
原子操作函數(shù)的第一個(gè)參數(shù)值對(duì)應(yīng)的都應(yīng)該是那個(gè)被操作的值烦感。比如,atomic.AddInt32函數(shù)的第一個(gè)參數(shù)膛堤,對(duì)應(yīng)的一定是那個(gè)要被增大的整數(shù)手趣。而且參數(shù)的類(lèi)型都是要求是指針類(lèi)型。*int32肥荔。
因?yàn)樵硬僮骱瘮?shù)需要的是被操作值的指針绿渣,而不是這個(gè)值本身;被傳入函數(shù)的參數(shù)值都會(huì)被復(fù)制燕耿,像這種基本類(lèi)型的值一旦被傳入函數(shù)中符,就已經(jīng)與函數(shù)外的那個(gè)值毫無(wú)關(guān)系了所以,傳入值本身沒(méi)有任何意義誉帅。unsafe.Pointer類(lèi)型雖然是指針類(lèi)型淀散,但是那些原子操作函數(shù)要操作的是這個(gè)指針值谭期,而不是它指向的那個(gè)值,所以需要的仍然是指向這個(gè)指針值的指針吧凉。
只要原子操作函數(shù)拿到了被操作值的指針隧出,就可以定位到存儲(chǔ)該值的內(nèi)存地址。只有這樣阀捅,它們才能夠通過(guò)底層的指令胀瞪,準(zhǔn)確地操作這個(gè)內(nèi)存地址上的數(shù)據(jù)。
原子減法
atomic只提供了add相關(guān)的方法饲鄙,并沒(méi)有提供減法凄诞。但是atomic.AddInt32函數(shù)的第二個(gè)參數(shù)代表差量,它的類(lèi)型是int32忍级,是有符號(hào)的帆谍。如果我們想做原子減法,那么把這個(gè)差量設(shè)置為負(fù)整數(shù)就可以了轴咱。
無(wú)符號(hào)類(lèi)型如:uint32可以下方法轉(zhuǎn)化:
方法一:
uint32(int32(-N))
方法二:
^uint32(-N-1))
其中的N代表由負(fù)整數(shù)表示的差量汛蝙。
交換與比較并交換
交換(swap)把新值賦給變量,并返回變量的舊值
比較并交換(compare and swap朴肺,簡(jiǎn)稱CAS)是有條件的交換操作窖剑,只有在條件滿足的情況下才會(huì)進(jìn)行值的交換。在進(jìn)行CAS操作的時(shí)候戈稿,函數(shù)會(huì)先判斷被操作變量的當(dāng)前值西土,是否與我們預(yù)期的舊值相等。如果相等鞍盗,它就把新值賦給該變量需了,并返回true以表明交換操作已進(jìn)行;否則就忽略交換操作般甲,并返回false肋乍。
CAS操作并不是單一的操作,而是一種操作組合欣除。應(yīng)用要更廣泛一些住拭,如自旋鎖
for {
if atomic.CompareAndSwapInt32(&num2, 10, 0) {
fmt.Println("The second number has gone to zero.")
break
}
time.Sleep(time.Millisecond * 500)
}
網(wǎng)上也有一些人用CAS做無(wú)鎖編程所謂無(wú)鎖編程只是將多條指令合并成了一條指令形成一個(gè)邏輯完備的最小單元挪略,通過(guò)兼容CPU指令執(zhí)行邏輯形成的一種多線程編程模型
載入與存儲(chǔ)
載入Load 原子性的讀取一個(gè)變量的值历帚,確保在不會(huì)讀到被修改到一半的值
存儲(chǔ)Store原子地存儲(chǔ)某個(gè)值的過(guò)程中,任何CPU都不會(huì)進(jìn)行針對(duì)同一個(gè)值的讀或?qū)懖僮?/p>
思考
問(wèn)題1:為什么不設(shè)計(jì)比較(compare)如果設(shè)計(jì)了比較(compare) 杠娱,在一段代碼中挽牢, 我們先 比較(compare) 得出結(jié)果后再 交換(swap)。將CAS操作分成兩步走還是原子是否安全摊求?
答案肯定是不安全禽拔,compare 是原子的 swap 是原子,單不代表這兩的組合是原子的。不設(shè)計(jì)compare原因應(yīng)該是意義不大睹栖,CAS操作本身也可以實(shí)現(xiàn)compare硫惕,而且compare后往往還需跟其他的操作,swap通常是最嘗被使用的野来。
問(wèn)題 2:假設(shè)我已經(jīng)保證了對(duì)一個(gè)變量的寫(xiě)操作都是原子操作恼除,比如:加或減、存儲(chǔ)曼氛、交換等等豁辉,那我對(duì)它進(jìn)行讀操作的時(shí)候,還有必要使用原子操作嗎舀患?
答案是很有必要其中的道理你可以對(duì)照一下讀寫(xiě)鎖徽级。為什么在讀寫(xiě)鎖保護(hù)下的寫(xiě)操作和讀操作之間是互斥的?這是為了防止讀操作讀到?jīng)]有被修改完的值聊浅,對(duì)嗎餐抢?
如果寫(xiě)操作還沒(méi)有進(jìn)行完,讀操作就來(lái)讀了低匙,那么就只能讀到僅修改了一部分的值弹澎。這顯然破壞了值的完整性,讀出來(lái)的值也是完全錯(cuò)誤的努咐。所以苦蒿,一旦你決定了要對(duì)一個(gè)共享資源進(jìn)行保護(hù),那就要做到完全的保護(hù)渗稍。不完全的保護(hù)基本上與不保護(hù)沒(méi)有什么區(qū)別