go語言提供的原子操作都是非侵入式的,它們由標準庫代碼包sync/atomic中的眾多函數代表。
我們調用sync/atomic中的幾個函數可以對幾種簡單的類型進行原子操作。這些類型包括int32,int64,uint32,uint64,uintptr,unsafe.Pointer,共6個。這些函數的原子操作共有5種:增或減,比較并交換、載入、存儲和交換它們提供了不同的功能,切使用的場景也有區別。
增或減
顧名思義,原子增或減即可實現對被操作值的增大或減少。因此該操作只能操作數值類型。
被用于進行增或減的原子操作都是以“Add”為前綴,并后面跟針對具體類型的名稱。
//方法源碼
func AddUint32(addr *uint32, delta uint32) (new uint32)
增
栗子:(在原來的基礎上加n)
atomic.AddUint32(&addr,n)
減
栗子:(在原來的基礎上加n(n為負數))
atomic.AddUint32(*addr,uint32(int32(n)))
//或
atomic.AddUint32(&addr,^uint32(-n-1))
比較并交換
比較并交換----Compare And Swap 簡稱CAS
他是假設被操作的值未曾被改變(即與舊值相等),并一旦確定這個假設的真實性就立即進行值替換
如果想安全的并發一些類型的值,我們總是應該優先使用CAS
//方法源碼
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
栗子:(如果addr和old相同,就用new代替addr)
ok:=atomic.CompareAndSwapInt32(&addr,old,new)
載入
如果一個寫操作未完成,有一個讀操作就已經發生了,這樣讀操作使很糟糕的。
為了原子的讀取某個值sync/atomic代碼包同樣為我們提供了一系列的函數。這些函數都以"Load"為前綴,意為載入。
//方法源碼
func LoadInt32(addr *int32) (val int32)
栗子
fun addValue(delta int32){
for{
v:=atomic.LoadInt32(&addr)
if atomic.CompareAndSwapInt32(&v,addr,(delta+v)){
break;
}
}
}
存儲
與讀操作對應的是寫入操作,sync/atomic也提供了與原子的值載入函數相對應的原子的值存儲函數。這些函數的名稱均以“Store”為前綴
在原子的存儲某個值的過程中,任何cpu都不會進行針對進行同一個值的讀或寫操作。如果我們把所有針對此值的寫操作都改為原子操作,那么就不會出現針對此值的讀操作讀操作因被并發的進行而讀到修改了一半的情況。
原子操作總會成功,因為他不必關心被操作值的舊值是什么。
//方法源碼
func StoreInt32(addr *int32, val int32)
栗子
atomic.StoreInt32(被操作值的指針,新值)
atomic.StoreInt32(&value,newaddr)
交換
原子交換操作,這類函數的名稱都以“Swap”為前綴。
與CAS不同,交換操作直接賦予新值,不管舊值。
會返回舊值
//方法源碼
func SwapInt32(addr *int32, new int32) (old int32)
栗子
atomic.SwapInt32(被操作值的指針,新值)(返回舊值)
oldval:=atomic.StoreInt32(&value,newaddr)