原子操作概述
原子操作函数
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)
StoreT和 LoadT常被用来实现并发运行的setter和getter方法
下面的例子展示了如何并发地递增一个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)
SwapT和StoreT函数类似,但是返回之前的旧值。
CompareAndSwapT函数在旧值和目标值的当前值相匹配时才会将目标值改为新值,返回 true;否则立即返回 false。
unsafe标准库包,go 不保证它的向后兼容性。
Value可以读取和修改任何类型的值。
也可以使用指针原子操作来对任何类型的值进行原子读取和修改,不过需要多一级指针的间接引用,两种方法有各自的好处和缺点。
总结
原子操作的几种方式
- 原子函数
- 基本类型
- 指针类型
- 原子方法(常用类型对应的原子类型)
Value- 自定义泛型