go 原子操作

123 阅读2分钟

原子操作概述

原子操作函数

func AddT(addr *T, delta T)(new T)
func LoadT(addr *T) (val T)
func StoreT(addr *T, val T)
func SwapT(addr *T, new T)(old T)
func CompareAndSwapT(addr *T, old, new T) (swapped bool)

StoreTLoadT常被用来实现并发运行的settergetter方法

下面的例子展示了如何并发地递增一个int32

func testAtomic() {
    var n int32
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
       wg.Add(1)
       go func() {
          atomic.AddInt32(&n, 1)
          // n++
       }()
    }
    wg.Wait()

    fmt.Println(atomic.LoadInt32(&n))
}

安全指针类型

func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

Value类型

func (*Value) Load() (x interface{})
func (*Value) Store(x interface{})
func (*Value) Swap(new interface{}) (old interface{})
func (*Value) CompareAndSwap(old, new interface{}) (swapped bool) 

原子类型

go1.19 引入了原子类型和相应的方法

例如,atomic.Int32提供以下原子操作的方法

func (*Int32) Add(delta int32) (new int32)
func (*Int32) Load() int32  // 原子地读取并返回变量的值
func (*Int32) Store(val int32)
func (*Int32) Swap(new int32) (old int32)
func (*Int32) CompareAndSwap(old, new int32) (swapped bool)

除了atomic.Int32类型,还有atomic.Uint32等等其他类型。 如果要通过Add实现无符号类型的减法,可通过^T(c-1)的小技巧,取该数的补码(先减 1 再取反 )。

自定义泛型

go 从1.18 版本开始支持自定义泛型

(*Pointer[T]) Load() *T
(*Pointer[T]) Store(val *T)
(*Pointer[T]) Swap(new *T) (old *T)
(*Pointer[T]) CompareAndSwap(old, new *T) (swapped bool)

SwapTStoreT函数类似,但是返回之前的旧值。 CompareAndSwapT函数在旧值和目标值的当前值相匹配时才会将目标值改为新值,返回 true;否则立即返回 false。

unsafe标准库包,go 不保证它的向后兼容性。

Value可以读取和修改任何类型的值。 也可以使用指针原子操作来对任何类型的值进行原子读取和修改,不过需要多一级指针的间接引用,两种方法有各自的好处和缺点。

总结

原子操作的几种方式

  • 原子函数
    • 基本类型
    • 指针类型
  • 原子方法(常用类型对应的原子类型)
  • Value
  • 自定义泛型