原子操作(不可分割的操作)提供比 mutex 更輕量的同步方式,適合單一變數的簡單同步。
sync/atomic 套件#
import "sync/atomic"
var counter int64
func increment() {
atomic.AddInt64(&counter, 1)
}
func getCount() int64 {
return atomic.LoadInt64(&counter)
}常用原子操作#
var value int64 = 10
// 增減
atomic.AddInt64(&value, 5) // value += 5
atomic.AddInt64(&value, -3) // value -= 3
// 讀取
v := atomic.LoadInt64(&value)
// 寫入
atomic.StoreInt64(&value, 100)
// Swap:回傳舊值
old := atomic.SwapInt64(&value, 200)
// Compare-And-Swap:條件交換
swapped := atomic.CompareAndSwapInt64(&value, 200, 300)
// 若 value == 200,則設為 300,回傳 true
// 否則不改變,回傳 false原子操作支援的型別:
int32、int64、uint32、uint64、uintptr、unsafe.Pointer。
atomic.Value#
用於原子地儲存與載入任意型別:
var config atomic.Value
// 儲存設定
config.Store(&Config{Debug: true})
// 載入設定
cfg := config.Load().(*Config)用 atomic.Value 實作無鎖快取
type Cache struct {
data atomic.Value // 儲存 map[string]string
}
func NewCache() *Cache {
c := &Cache{}
c.data.Store(make(map[string]string))
return c
}
func (c *Cache) Get(key string) string {
m := c.data.Load().(map[string]string)
return m[key]
}
func (c *Cache) Set(key, value string) {
for {
oldMap := c.data.Load().(map[string]string)
newMap := make(map[string]string, len(oldMap)+1)
for k, v := range oldMap {
newMap[k] = v
}
newMap[key] = value
if c.data.CompareAndSwap(oldMap, newMap) {
return
}
// CAS 失敗,重試
}
}寫入時整個 map 複製一份再 CAS 換上去,讀取端永遠不需要鎖。寫多時開銷大,但極端讀多寫少的場景非常划算。