Go语言原子操作

一. 原子操作

什么是原子操作呢?原子操作就是具备原子性的操作,一个或者多个操作在cpu的执行过程中不被中断的特性,称为原子性,这些操作对外表现为一个不可分割的整体,要么全部执行,要么全部不执行;为了保证并发安全,可以使用Go语言的sync包中的Mutex类型调用加锁和解锁的方法,但是加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高,对于基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作在用户态就可以完成,所以性能比加锁操作更好,更能利用计算机的优势,对应的原子操作在内置的标准库sync/atomic可以找到,atomic包提供了底层的原子级内存操作,对于同步算法的实现很有用,但是这些函数必须谨慎以保证正确使用。除了某些特殊的底层应用,使用通道或者sync包的函数/类型实现同步更好,这个包提供了五种类型的原子操作:① 交换操作,由 SwapT类型的函数实现;② 比较和交换操作,由CompareAndSwapT类型的函数实现;③ 加操作,由AddT类型的函数实现,保证对操作数进行原子的增减,支持的类型有int32,int64,uint32,uint64,unitptr,;④ 载入操作,由LoadT类型的函数实现;⑤ 存储操作,由StoreT类型的函数实现;需要注意的是,所有原子操作方法的被操作数形参必须是指针类型,通过指针变量可以获取被操作数在在内存中的地址,从而施加特殊的cpu指令,确保同一时间只有一个goroutine能够进行操作,我们可以通过下面的例子比较一下互斥锁和原子操作的性能:

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

var x int64
var l sync.Mutex
var wg sync.WaitGroup

// 普通版加函数
func add() {
	x = x + 1
	wg.Done()
}

// 互斥锁版加函数
func mutexAdd() {
	l.Lock()
	x++
	l.Unlock()
	wg.Done()
}

// 原子操作版加函数
func atomicAdd() {
	defer wg.Done()
	atomic.AddInt64(&x, 1)
}

func main() {
	start := time.Now()
	for i := 0; i < 1000000; i++ {
		wg.Add(1)
		// go add() // 普通版add函数 不是并发安全的
		// go mutexAdd() // 加锁版add函数 是并发安全的,但是加锁性能开销大
		go atomicAdd() // 原子操作版add函数 是并发安全,性能优于加锁版
	}
	wg.Wait()
	end := time.Now()
	fmt.Println(x)
	fmt.Println(end.Sub(start))
}

二. 互斥锁与原子操作的区别

区别:互斥锁用来保护一段逻辑,原子操作用于对于一个变量的更新保护;
底层实现:互斥锁由操作系统的调度器来实现,atomic包中的原子操作则由底层硬件指令直接提供支持,这些指令在执行的过程中不允许中断,所以原子操作是lock-free情况下保证并发安全,并且它的性能可以做到随cpu个数的增加而线性扩展。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值