千家信息网

Go语言原子操作及互斥锁的区别是什么

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇内容介绍了"Go语言原子操作及互斥锁的区别是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!原
千家信息网最后更新 2026年11月02日Go语言原子操作及互斥锁的区别是什么

本篇内容介绍了"Go语言原子操作及互斥锁的区别是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

原子操作就是不可中断的操作,外界是看不到原子操作的中间状态,要么看到原子操作已经完成,要么看到原子操作已经结束。在某个值的原子操作执行的过程中,CPU绝对不会再去执行其他针对该值的操作,那么其他操作也是原子操作。

Go语言中提供的原子操作都是非侵入式的,在标准库代码包sync/atomic中提供了相关的原子函数。

增或减

用于增或减的原子操作的函数名称都是以"Add"开头的,后面跟具体的类型名,比如下面这个示例就是int64类型的原子减操作

func main() {   var  counter int64 =  23   atomic.AddInt64(&counter,-3)   fmt.Println(counter)}---output---20

原子函数的第一个参数都是指向变量类型的指针,是因为原子操作需要知道该变量在内存中的存放位置,然后加以特殊的CPU指令,也就是说对于不能取得内存存放地址的变量是无法进行原子操作的。第二个参数的类型会自动转换为与第一个参数相同的类型。此外,原子操作会自动将操作后的值赋值给变量,无需我们自己手动赋值了。

对于 atomic.AddUint32() 和 atomic.AddUint64() 的第二个参数为 uint32 与 uint64,因此无法直接传递一个负的数值进行减法操作,Go语言提供了另一种方法来迂回实现:使用二进制补码的特性

注意:unsafe.Pointer 类型的值无法被加减。

比较并交换(Compare And Swap)

简称CAS,在标准库代码包sync/atomic中以"Compare And Swap"为前缀的若干函数就是CAS操作函数,比如下面这个

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)

第一个参数的值是这个变量的指针,第二个参数是这个变量的旧值,第三个参数指的是这个变量的新值。

运行过程:调用CompareAndSwapInt32 后,会先判断这个指针上的值是否跟旧值相等,若相等,就用新值覆盖掉这个值,若相等,那么后面的操作就会被忽略掉。返回一个 swapped 布尔值,表示是否已经进行了值替换操作。

与锁有不同之处:锁总是假设会有并发操作修改被操作的值,而CAS总是假设值没有被修改,因此CAS比起锁要更低的性能损耗,锁被称为悲观锁,而CAS被称为乐观锁。

CAS的使用示例

var value int32func AddValue(delta int32)  {   for {      v:= value      if atomic.CompareAndSwapInt32(&value,v,(v+delta)) {         break      }   }}

由示例可以看出,我们需要多次使用for循环来判断该值是否已被更改,为了保证CAS操作成功,仅在 CompareAndSwapInt32 返回为 true时才退出循环,这跟自旋锁的自旋行为相似。

载入与存储

对一个值进行读或写时,并不代表这个值是最新的值,也有可能是在在读或写的过程中进行了并发的写操作导致原值改变。为了解决这问题,Go语言的标准库代码包sync/atomic提供了原子的读取(Load为前缀的函数)或写入(Store为前缀的函数)某个值

将上面的示例改为原子读取

var value int32func AddValue(delta int32)  {   for {      v:= atomic.LoadInt32(&value)      if atomic.CompareAndSwapInt32(&value,v,(v+delta)) {         break      }   }}

原子写入总会成功,因为它不需要关心原值是什么,而CAS中必须关注旧值,因此原子写入并不能代替CAS,原子写入包含两个参数,以下面的StroeInt32为例:

//第一个参数是被操作值的指针,第二个是被操作值的新值func StoreInt32(addr *int32, val int32)

交换

这类操作都以"Swap"开头的函数,称为"原子交换操作",功能与之前说的CAS操作与原子写入操作有相似之处。

func SwapInt32(addr *int32, new int32) (old int32)

以 SwapInt32 为例,第一个参数是int32类型的指针,第二个是新值。原子交换操作不需要关心原值,而是直接设置新值,但是会返回被操作值的旧值。

原子值

Go语言的标准库代码包sync/atomic中有一个叫做Value的原子值,它是一个结构体类型,用于存储需要原子读写的值,结构体如下

// Value提供原子加载并存储一致类型的值。// Value的零值从Load返回nil。//调用Store后,不得复制值。//首次使用后不得复制值。type Value struct {   v interface{}}

可以看出结构体内是一个 v interface{},也就是说 该Value原子值可以保存任何类型的需要原子读写的值。

使用方式如下:

var Atomicvalue  atomic.Value

该类型有两个公开的指针方法

//原子的读取原子值实例中存储的值,返回一个 interface{} 类型的值,且不接受任何参数。//若未曾通过store方法存储值之前,会返回nilfunc (v *Value) Load() (x interface{})//原子的在原子实例中存储一个值,接收一个 interface{} 类型(不能为nil)的参数,且不会返回任何值func (v *Value) Store(x interface{})

一旦原子值实例存储了某个类型的值,那么之后Store存储的值就必须是与该类型一致,否则就会引发panic。

严格来讲,atomic.Value类型的变量一旦被声明,就不应该被复制到其他地方。比如:作为源值赋值给其他变量,作为参数传递给函数,作为结果值从函数返回,作为元素值通过通道传递,这些都会造成值的复制。

但是atomic.Value类型的指针类型变量就不会存在这个问题,原因是对结构体的复制不但会生成该值的副本,还会生成其中字段的副本,这样那么并发引发的值变化都与原值没关系了。

看下面这个小示例

func main() {   var Atomicvalue  atomic.Value   Atomicvalue.Store([]int{1,2,3,4,5})   anotherStore(Atomicvalue)   fmt.Println("main: ",Atomicvalue)}func anotherStore(Atomicvalue atomic.Value)  {   Atomicvalue.Store([]int{6,7,8,9,10})   fmt.Println("anotherStore: ",Atomicvalue)}---output---anotherStore:  {[6 7 8 9 10]}main:  {[1 2 3 4 5]}

原子操作与互斥锁的区别

互斥锁是一种数据结构,使你可以执行一系列互斥操作。而原子操作是互斥的单个操作,这意味着没有其他线程可以打断它。那么就Go语言里atomic包里的原子操作和sync包提供的同步锁有什么不同呢?

首先atomic操作的优势是更轻量,比如CAS可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作。这可以大大的减少同步对程序性能的损耗。

原子操作也有劣势。还是以CAS操作为例,使用CAS操作的做法趋于乐观,总是假设被操作值未曾被改变(即与旧值相等),并一旦确认这个假设的真实性就立即进行值替换,那么在被操作值被频繁变更的情况下,CAS操作并不那么容易成功。而使用互斥锁的做法则趋于悲观,我们总假设会有并发的操作要修改被操作的值,并使用锁将相关操作放入临界区中加以保护。

所以总结下来原子操作与互斥锁的区别有:

互斥锁是一种数据结构,用来让一个线程执行程序的关键部分,完成互斥的多个操作。
原子操作是针对某个值的单个互斥操作。
可以把互斥锁理解为悲观锁,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
atomic包提供了底层的原子性内存原语,这对于同步算法的实现很有用。这些函数一定要非常小心地使用,使用不当反而会增加系统资源的开销,对于应用层来说,最好使用通道或sync包中提供的功能来完成同步操作。

针对atomic包的观点在Google的邮件组里也有很多讨论,其中一个结论解释是:

应避免使用该包装。或者,阅读C ++ 11标准的"原子操作"一章;如果您了解如何在C ++中安全地使用这些操作,那么你才能有安全地使用Go的 sync/atomic包的能力。

"Go语言原子操作及互斥锁的区别是什么"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

原子 类型 参数 函数 变量 存储 语言 指针 结构 标准 示例 线程 代码 原值 过程 同步 安全 悲观 成功 内存 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广州万易互联网科技有限公司 鸠鸠互联网科技 互联网科技有限公司图片 大白互联网科技 互联网科技大会案例 太原新华互联网科技学校怎么样 青岛中康互联网医疗科技有限公司 成都互联网科技公司100强 互联网时代科技改变生活 允成互联网科技有限公司融资 浙南科技城互联网大厦建设工程 阿里互联网科技企业 科技互联网的核心竞争力 雨阳互联网卡科技有限公司 互联网科技年终奖 互联网金融运营模式中的金融科技 兰石爱特工业互联网科技分公司 美国五大互联网科技公司市值多少 山东蛋芒互联网科技有限公司 互联网科技创新方面的资料 平安科技 互联网 互联网科技ppt模板 多迪互联网科技有限公司 北京华百购互联网科技有限公司 互联网企业和科技型企业 上海京穆互联网科技有限公司 小米互联网科技园入驻 三亚科技创新互联网基地 河南商务圈互联网科技有限公司 互联网 科技节活动 微视频大赛
0