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循环不断判断会降低性能。 

 

posted on 2020-04-07 12:59  thotf  阅读(251)  评论(0)    收藏  举报

导航