atomic包提供了底层的原子级内存操作,对于同步算法的实现很有用

type Value

value提供一致类型值的原子加载和存储。值的零值从加载返回nil。一旦调用了存储,就不能复制值

  1. type Value struct {
  2. // contains filtered or unexported fields
  3. }

func (v Value) Load() (x interface{}) 返回的存储的值。如果没有调用存储这个值,它会返回nil
func (v
Value) Store(x interface{}) 将值设置为x,如果类型不一致会引起恐慌

取值

func LoadInt32(addr int32) (val int32)
func LoadInt64(addr
int64) (val int64)
func LoadUint32(addr uint32) (val uint32)
func LoadUint64(addr
uint64) (val uint64)
func LoadUintptr(addr uintptr) (val uintptr)
func LoadPointer(addr
unsafe.Pointer) (val unsafe.Pointer)

存值

func StoreInt32(addr int32, val int32)
func StoreInt64(addr
int64, val int64)
func StoreUint32(addr uint32, val uint32)
func StoreUint64(addr
uint64, val uint64)
func StoreUintptr(addr uintptr, val uintptr)
func StorePointer(addr
unsafe.Pointer, val unsafe.Pointer)

增减

func AddInt32(addr int32, delta int32) (new int32):原子性的将val的值添加到addr并返回新值
func AddInt64(addr int64, delta int64) (new int64)
func AddUint32(addr
uint32, delta uint32) (new uint32)
func AddUint64(addr uint64, delta uint64) (new uint64)
func AddUintptr(addr
uintptr, delta uintptr) (new uintptr)

交换

func SwapInt32(addr int32, new int32) (old int32):原子性的将新值保存到addr并返回旧值
func SwapInt64(addr int64, new int64) (old int64)
func SwapUint32(addr
uint32, new uint32) (old uint32)
func SwapUint64(addr uint64, new uint64) (old uint64)
func SwapUintptr(addr
uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)

比较并交换

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

  • 原子性的比较addr和old,如果相同则将new赋值给addr并返回真

func CompareAndSwapInt64(addr int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr
uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr
uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

并发带来的问题

通过以下代码,我们可以了解到并发安全的问题

  1. var Count int64
  2. func main() {
  3. for {
  4. var wg sync.WaitGroup
  5. for i := 0; i<100 ;i++ {
  6. wg.Add(1)
  7. go func() {
  8. defer wg.Done()
  9. Count += 1
  10. }()
  11. }
  12. wg.Wait()
  13. fmt.Println(Count)
  14. time.Sleep(1*time.Second)
  15. Count = 0
  16. }
  17. }
  18. -------------------------------------
  19. 100
  20. 100
  21. 99
  22. 97
  23. 99
  24. 98
  25. 98

理想的情况下,每次输出都应该是100,但实际并非这样,这就是并发带来的安全问题,count += 1,实际为count = count +1,可想在并发的过程中,如线程1 拿到count是 97 count + 1 后是98,在把98赋值给count时cpu执行权被抢走,线程2进来开始运行,此时count还是97,count + 1后是98,那后把98赋值给count,线程2结束,此时线程1重新获得cpu执行权,将98赋值给count,这样实际上count赋值为98执行了两次

  1. var Count int64
  2. func main() {
  3. for {
  4. var wg sync.WaitGroup
  5. for i := 0; i<100 ;i++ {
  6. wg.Add(1)
  7. go func() {
  8. defer wg.Done()
  9. atomic.AddInt64(&Count,1)
  10. }()
  11. }
  12. wg.Wait()
  13. fmt.Println(Count)
  14. time.Sleep(1*time.Second)
  15. Count = 0
  16. }
  17. }
  18. ----------------------------------------
  19. 100
  20. 100
  21. 100
  22. 100
  23. 100
  24. 100
  25. 100

使用atomic.AddInt64原子性的赋值 ,就可以避免并发带来的安全问题