go标准库sync/atomic
atomic 是提供原子操作的模块。
所谓原子是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束不会切换其他线程。
上标准库:


还有这个 atmoc.Value 这是原子值 后面会写很重要,不知道为什么标准库文档里没看到。
标准库上面的所有操作看函数名就很清楚了,
ADD、store、Load、swap、CAS
使用方法都是一样的,只是实际操作数的类型是不同的而已。所以只对每种类型举例一次其他以此类推即可。
ADD 系列函数,是 ”原子性的” 对一个value 进行+n的操作。
这个+n是在原有value的基础上进行+操作。
var a int32 = 5 atomic.AddInt32(&a,1) fmt.Println(a)
那进行减操作只要第二个操作数改成负数即可。
var a int32 = 5 atomic.AddInt32(&a,-1) //这样就是减1了 fmt.Println(a)
Load 和Store系列函数,许多变量的读写无法在一个时钟周期内完成,而此时执行可能会被调度到其他线程,无法保证并发安全。
Load 保证读取的不是正在写入的值。
Store 保证原子性的储存一个值。
=。=没必要举例吧。
Swap 系列函数,“原子性的” 将新值保存到*addr并返回旧值。
Store 、Swap、ADD 的不同点。
Store是储存一个值不考虑原值是什么
Swap是进行“交换”,用新值和原值交换,把原值返回。
ADD是在原值的基础上增加一个单位,并返回新值。
type SafetyArray struct{
array []int
len uint
atomic.Value
}
func NewSafetyArray(len uint)SafetyArray{ return SafetyArray{ array: make([]int,len), len: len, Value: atomic.Value{}, } } func (arr *SafetyArray)checkIndex(n uint)bool { if n >= arr.len{ return false } return true } func (arr *SafetyArray)Set(index uint,n int){ if !arr.checkIndex(index){ panic("索引不在规定范围内") } oldArray := arr.Value.Load().([]int) //Value.Store 的参数是interface所以读取数据需要做一下断言 newArray := make([]int,arr.len) //不要再原值上进行操作 不然会导致安全保护失效。 copy(newArray,oldArray) arr.Value.Store(newArray) } func (arr *SafetyArray)Get(index uint)int{ if !arr.checkIndex(index){ panic("索引不在规定范围内") } v := arr.Value.Load().([]int)[index] return v }
上面使用 atomic.Value实现了一个安全并发数组。
atomic.Value 主要是使用两个 方法
Load()interface
Store(interface)
使用的时候要注意一些问题
Store 1、作为参数传入该方法的值不能为nil
2、作为参数传入该方法的值比如与之前传入的值类型一样
严格注意不可以将atomic.Value 到处复制 不然会报错,也会失去并发安全保护。但是 指针类型可以传递。
什么是CAS?
即比较再交换
var a int32 = 5
for {
if atomic.CompareAndSwapInt32(&a, 5, 6) {
break
}
}
atomic.CompareAndSwapInt32.(addr *int32,old int32,new int32)
addr是要交换值的地址,old是当前函数期望的*addr的值,如果符合期望那就将当前a值换为new的值
在少量的并发中 CAS 可以大大降低性能损耗。
在高并发中很难第一时间完成期望的交换,所以需要for循环不断判断会降低性能。
浙公网安备 33010602011771号