Golang 标准库 sync/atomic

1. 基本概念

原子操作:指的是一个操作或一系列操作在被CPU调度的时候不可中断。即在并发中,保证多CPU对同一块内存的操作是原子性的。

原子操作的实现方式:

  1. 总线加锁:CPU和其他硬件的通信通过总线控制,所以可以通过Lock总线的方式实现原子操作,但这样会阻塞其他硬件对CPU的访问,开销太大
  2. 缓存锁定:频繁使用的内存会被处理器放进高速缓存中,那么原子操作就可以直接在处理器的高速缓存中进行,主要依靠缓存的一致性来确保其原子性

Golang 中的原子操作:sync/atomic

能够进行原子操作的类型:int32, int64, uint32, uint64, uintptr, unsafe.Pointer

五种操作函数:增或减、比较并交换、载入、存储、交换

原子操作比锁更为高效。

2. 原子操作 vs 锁

  • 加锁比较耗时,需要上下文切换。即使是goroutine也需要上下文切换
  • 只针对基本类型,可使用原子操作保证线程安全
  • 原子操作在用户态完成,性能比互斥锁要高
  • 原子操作步骤简单,不需要加锁-操作-解锁

3. 五种操作

  • 增或减 (Add)
  • 比较并交换 (CAS, Compare & Swap)
  • 载入 (Load)
  • 存储 (Store)
  • 交换 (Swap)

3.1 增或减

func AddInt64(addr *int64, delta int64) (new int64)
func main() {
	var n int64

	for i := 0; i <= 100; i++ {
		go func(i int) {
			//n += int64(i)  // 无法保证原子性
			atomic.AddInt64(&n, int64(i))
			time.Sleep(time.Millisecond)
		}(i)
	}

	time.Sleep(time.Second)
	fmt.Println(atomic.LoadInt64(&n))
}

3.2 载入

当读取的时候,任何其他CPU操作都无法对该变量进行读写

func LoadInt64(addr *int64) (val int64)

3.3 比较并交换

func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)

CAS操作,在进行交换前,首先确保变量的值未被更改,即仍然保持参数 old 所记录的值,满足此前提下才进行交换操作。CAS的做法类似操作数据库时常见的乐观锁机制。

注意:当有大量 goroutine 对变量进行读写操作时,可能导致CAS操作无法成功,此时要利用for循环多次尝试。

var N int64

func atomicAddOp(i int64) {
	// 可能不成功的操作
	//tmp := atomic.LoadInt64(&N)
	//swapped := atomic.CompareAndSwapInt64(&N, tmp, tmp+i)
	//fmt.Printf("%d try to CAS: %v\n", tmp, swapped)

	for {
		tmp := atomic.LoadInt64(&N)
		swapped := atomic.CompareAndSwapInt64(&N, tmp, tmp+i)
		fmt.Printf("%d try to CAS: %v\n", tmp, swapped)
		if swapped {
			break
		}
	}

	time.Sleep(time.Millisecond)
}

func main() {
	for i := 0; i <= 100; i++ {
		go atomicAddOp(int64(i))
	}

	time.Sleep(time.Second)
	fmt.Println(atomic.LoadInt64(&N))
}

3.4 存储

func StoreInt64(addr *int64, val int64)

此操作可确保写变量的原子性,避免其他操作读到修改变量过程中的脏数据。

3.5 交换

func SwapInt64(addr *int64, new int64) (old int64)

4. 原子值

type Value struct {
	v interface{}
}

func (v *Value) Load() (x interface{})

func (v *Value) Store(x interface{})
type AtomicArray interface {
	Set(idx uint32, elem int) error
	Get(idx uint32) (int, error)
	Len() uint32
}

type Array struct {
	value  atomic.Value
	length uint32
}

func (a *Array) checkIndex(idx uint32) (err error) {
	if a.length <= idx {
		err = errors.New("array out of range")
	}
	return
}

func NewArray(arr []int) Array {
	val := atomic.Value{}
	val.Store(arr)
	return Array{val, uint32(len(arr))}
}

func (a *Array) Set(idx uint32, elem int) (err error) {
	if err = a.checkIndex(idx); err != nil {
		return
	}

	newArr := make([]int, a.length)
	copy(newArr, a.value.Load().([]int))
	newArr[idx] = elem
	a.value.Store(newArr)

	return
}

func (a *Array) Len() uint32 {
	return a.length
}

func (a *Array) Get(idx uint32) (int, error) {
	if err := a.checkIndex(idx); err != nil {
		return 0, err
	}

	arr := a.value.Load().([]int)
	return arr[idx], nil
}

func main() {
	a := NewArray([]int{5, 3, 6, 2, 8})

	fmt.Println(a.length)

	elem, err := a.Get(3)
	if err != nil {
		panic(err)
	}
	fmt.Println(elem)

	err = a.Set(3, 10)
	if err != nil {
		panic(err)
	}

	elem, err = a.Get(3)
	if err != nil {
		panic(err)
	}
	fmt.Println(elem)
}

5. 实例

5.1 单例

type singleton struct{}

var (
	instance    *singleton
	initialized uint32
	mu          sync.Mutex
)

func Instance() *singleton {
	if atomic.LoadUint32(&initialized) == 1 {
		return instance
	}

	mu.Lock()
	defer mu.Unlock()

	if instance == nil {
		defer atomic.StoreUint32(&initialized, 1)
		instance = &singleton{}
	}

	return instance
}

基于sync.Once 实现:

type singleton struct{}

var (
	instance *singleton
	once     sync.Once
)

func Instance() *singleton {
	once.Do(func() {
		instance = &singleton{}
	})

	return instance
}

```sync.Once`源码:

type Once struct {
	m    sync.Mutex
	done uint32
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 1 {
		return
	}

	o.m.Lock()
	defer o.m.Unlock()

	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

5.2 处理配置

func loadConfig() map[string]string {
	return make(map[string]string)
}

func requests() chan int {
	return make(chan int)
}

func main() {
	var config atomic.Value

	// 初始化配置信息
	config.Store(loadConfig())

	// 启动一个协程,刷新配置信息
	go func() {
		for {
			time.Sleep(3 * time.Second)
			config.Store(loadConfig())
		}
	}()

	// 工作线程读取配置信息
	for i := 0; i < 10; i++ {
		go func() {
			for r := range requests() {
				c := config.Load()
				// ...
			}
		}()
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值