- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Golang 五种原子性操作的用法详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
。
本文我们详细聊一下 Go 语言的原子操作的用法,啥是原子操作呢?顾名思义,原子操作就是具备原子性的操作... 是不是感觉说了跟没说一样,原子性的解释如下:
一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为 原子性(atomicity) 。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态.
CPU 执行一系列操作时不可能不发生中断,但如果我们在执行多个操作时,能让他们的 中间状态对外不可见 ,那我们就可以宣称他们拥有了"不可分割”的原子性.
类似的解释我们在数据库事务的 ACID 概念里也听过.
Go 语言通过内置包 sync/atomic 提供了对原子操作的支持,其提供的原子操作有以下几大类:
LoadXXXType
,支持的类型除了基础类型外还支持 Pointer
,也就是支持载入任何类型的指针。Store
开头,支持的类型跟载入操作支持的那些一样。交换,这个简单粗暴一些,不比较直接交换,这个操作很少会用.
平日里,在并发编程里,Go语言 sync 包里的同步原语 Mutex 是我们经常用来保证并发安全的,那么他跟 atomic 包里的这些操作有啥区别呢?在我看来他们在使用目的和底层实现上都不一样:
使用目的:互斥锁是用来保护一段逻辑,原子操作用于对一个变量的更新保护.
Mutex
由 操作系统 的调度器实现,而 atomic
包中的原子操作则由 底层硬件指令 直接提供支持,这些指令在执行的过程中是不允许中断的,因此原子操作可以在 lock-free
的情况下保证并发安全,并且它的性能也能做到随 CPU
个数的增多而线性扩展。对于一个变量更新的保护,原子操作通常会更有效率,并且更能利用计算机多核的优势.
比如下面这个,使用互斥锁的并发计数器程序:
把 Mutex 改成用方法 atomic.AddInt32(&a, 1) 调用,在不加锁的情况下仍然能确保对变量递增的并发安全.
可以在本地运行以上这两段代码,可以观察到计数器的结果都最后都是 1000000 ,都是线程安全的.
上面的例子除了增加操作外我们还演示了载入操作,接下来我们来看一下 CAS 操作.
该操作简称 CAS (Compare And Swap)。这类操作的前缀为 CompareAndSwap
该操作在 进行交换前首先确保被操作数的值未被更改,即仍然保存着参数 old 所记录的值,满足此前提条件下才进行交换操作 。 CAS 的做法类似操作数据库时常见的乐观锁机制.
需要注意的是,当有大量的goroutine 对变量进行读写操作时,可能导致 CAS 操作无法成功,这时可以利用 for 循环多次尝试.
上面我只列出了比较典型的 int32 和 unsafe.Pointer 类型的 CAS 方法,主要是想说除了读数值类型进行比较交换,还支持对指针进行比较交换.
unsafe.Pointer提供了绕过Go语言指针类型限制的方法,unsafe指的并不是说不安全,而是说官方并不保证向后兼容.
上面的示例并不是在并发环境下进行的 CAS ,只是为了演示效果,先把被操作数设置成了 Old Pointer .
其实 Mutex 的底层实现也是依赖原子操作中的 CAS 实现的,原子操作的 atomic 包相当于是 sync 包里的那些同步原语的实现依赖.
比如互斥锁 Mutex 的结构里有一个 state 字段,其是表示锁状态的状态位.
为了方便理解,我们在这里将它的状态定义为0和1,0代表目前该锁空闲,1代表已被加锁,以下是 sync.Mutex 中 Lock 方法的部分实现代码.
在 atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) 中, m.state 代表锁的状态,通过 CAS 方法,判断锁此时的状态是否空闲( m.state==0 ),是,则对其加锁( mutexLocked 常量的值为1).
atomic 包里提供了一套 Store 开头的方法,用来保证各种类型变量的并发写安全,避免其他操作读到了修改变量过程中的脏数据.
这些操作方法的定义与上面介绍的那些操作的方法类似,我就不再演示怎么使用这些方法了.
值得一提的是如果你想要并发安全的设置一个结构体的多个字段,除了把结构体转换为指针,通过 StorePointer 设置外,还可以使用 atomic 包后来引入的 atomic.Value ,它在底层为我们完成了从具体指针类型到 unsafe.Pointer 之间的转换.
有了 atomic.Value 后,它使得我们可以不依赖于不保证兼容性的 unsafe.Pointer 类型,同时又能将任意数据类型的读写操作封装成原子性操作(中间状态对外不可见).
atomic.Value 类型对外暴露了两个方法:
c := v.Load()
- 读操作,从线程安全的 v
中读取上一步存放的内容。1.17 版本我看还增加了 Swap 和 CompareAndSwap 方法.
简洁的接口使得它的使用也很简单,只需将需要做并发保护的变量读取和赋值操作用 Load() 和 Store() 代替就行了.
由于 Load() 返回的是一个 interface{} 类型,所以在使用前我们记得要先转换成具体类型的值,再使用。下面是一个简单的例子演示 atomic.Value 的用法.
你也可以试试,不用 atomic.Value ,直接给 Rectange 类型的指针变量赋值,看看在并发条件下,两个字段的值是不是能跟预期的一样变成10和15.
本文详细介绍了Go语言原子操作 atomic 包中会被高频使用的操作的使用场景和用法,当然我并没有罗列 atomic 包里所有操作的用法,主要是考虑到有的用到的地方实在不多,或者是已经被更好的方式替代,还有就是觉得确实没必要,看完本文的内容相信你已经完全具备自行探索 atomic 包的能力了.
再强调一遍,原子操作由 底层硬件 支持,而锁则由操作系统的 调度器 实现。锁应当用来保护一段逻辑,对于一个变量更新的保护,原子操作通常会更有效率,并且更能利用计算机多核的优势,如果要更新的是一个复合对象,则应当使用 atomic.Value 封装好的实现.
给网管个星标第一时间吸我的知识 :point_up_2
原文链接:https://mp.weixin.qq.com/s?__biz=MzUzNTY5MzU2MA==&mid=2247489229&idx=1&sn=3674ab103ec4dd704f0e9d1a784eeed4&utm_source=tuicool&utm_medium=referral 。
最后此篇关于Golang 五种原子性操作的用法详解的文章就讲到这里了,如果你想了解更多关于Golang 五种原子性操作的用法详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在努力做到这一点 在我的操作中从数据库获取对象列表(确定) 在 JSP 上打印(确定) 此列表作为 JSP 中的可编辑表出现。我想修改然后将其提交回同一操作以将其保存在我的数据库中(失败。当我使用
我有以下形式的 Linq to Entities 查询: var x = from a in SomeData where ... some conditions ... select
我有以下查询。 var query = Repository.Query() .Where(p => !p.IsDeleted && p.Article.ArticleSections.Cou
我正在编写一个应用程序包,其中包含一个主类,其中主方法与GUI类分开,GUI类包含一个带有jtabbedpane的jframe,它有两个选项卡,第一个选项卡包含一个jtable,称为jtable1,第
以下代码产生错误 The nested query is not supported. Operation1='Case' Operation2='Collect' 问题是我做错了什么?我该如何解决?
我已经为 HA redis 集群(2 个副本、1 个主节点、3 个哨兵)设置了本地 docker 环境。只有哨兵暴露端口(10021、10022、10023)。 我使用的是 stackexchange
我正在 Desk.com 中构建一个“集成 URL”,它使用 Shopify Liquid 模板过滤器语法。对于开始日期为 7 天前而结束日期为现在的查询,此 URL 需要包含“开始日期”和“结束日期
你一定想过。然而情况却不理想,python中只能使用类似于 i++/i--等操作。 python中的自增操作 下面代码几乎是所有程序员在python中进行自增(减)操作的常用
我需要在每个使用 github 操作的手动构建中显示分支。例如:https://gyazo.com/2131bf83b0df1e2157480e5be842d4fb 我应该显示分支而不是一个。 最佳答
我有一个关于 Perl qr 运算符的问题: #!/usr/bin/perl -w &mysplit("a:b:c", /:/); sub mysplit { my($str, $patt
我已经使用 ArgoUML 创建了一个 ERD(实体关系图),我希望在一个类中创建两个操作,它们都具有 void 返回类型。但是,我只能创建一个返回 void 类型的操作。 例如: 我能够将 book
Github 操作仍处于测试阶段并且很新,但我希望有人可以提供帮助。我认为可以在主分支和拉取请求上运行 github 操作,如下所示: on: pull_request push: b
我正在尝试创建一个 Twilio 工作流来调用电话并记录用户所说的内容。为此,我正在使用 Record,但我不确定要在 action 参数中放置什么。 尽管我知道 Twilio 会发送有关调用该 UR
我不确定这是否可行,但值得一试。我正在使用模板缓冲区来减少使用此算法的延迟渲染器中光体积的过度绘制(当相机位于体积之外时): 使用廉价的着色器,将深度测试设置为 LEQUAL 绘制背面,将它们标记在模
有没有聪明的方法来复制 和 重命名 文件通过 GitHub 操作? 我想将一些自述文件复制到 /docs文件夹(:= 同一个 repo,不是远程的!),它们将根据它们的 frontmatter 重命名
我有一个 .csv 文件,其中第一列包含用户名。它们采用 FirstName LastName 的形式。我想获取 FirstName 并将 LastName 的第一个字符添加到它上面,然后删除空格。然
Sitecore 根据 Sitecore 树中定义的项目名称生成 URL, http://samplewebsite/Pages/Sample Page 但我们的客户有兴趣降低所有 URL(页面/示例
我正在尝试进行一些计算,但是一旦我输入金额,它就会完成。我只是希望通过单击按钮而不是自动发生这种情况。 到目前为止我做了什么: Angular JS - programming-fr
我的公司创建了一种在环境之间移动文件的复杂方法,现在我们希望将某些构建的 JS 文件(已转换和缩小)从一个 github 存储库移动到另一个。使用 github 操作可以实现这一点吗? 最佳答案 最简
在我的代码中,我创建了一个 JSONArray 对象。并向 JSONArray 对象添加了两个 JSONObject。我使用的是 json-simple-1.1.jar。我的代码是 package j
我是一名优秀的程序员,十分优秀!