1、概述
1.1 基本概念
Golang 中的原子操作:sync/atomic包
能够进行原子操作的类型:int32, int64, uint32, uint64, uintptr, unsafe.Pointer
五种操作函数:增或减、比较并交换、载入、存储、交换
原子操作比锁更为高效。
1.2 原子操作 vs 锁
1.3 五种操作
- 增或减 (Add)
- 比较并交换 (CAS, Compare & Swap)
- 载入 (Load)
- 存储 (Store)
- 交换 (Swap)
1.4 最小案例
package main import ( "sync" "fmt" ) var count int func add(wg *sync.WaitGroup) { defer wg.Done() count++ } func main() { wg := sync.WaitGroup{} wg.Add(1000) for i := 0; i < 1000; i++ { go add(&wg) } wg.Wait() fmt.Println(count) }
count
不会等于1000,因为count++
这一步实际是三个操作:
- 从内存读取
count
- cpu更新
count = count + 1
- 写入
count
到内存
因此就会出现多个goroutine读取到相同的数值,然后更新同样的数值到内存,导致最终结果比预期少。
2、sync/atomic包使用
Go语言提供的原子操作都是非入侵式的,由标准库中sync/aotomic
中的众多函数代表
atomic包中支持六种类型
int32
uint32
int64
uint64
uintptr
unsafe.Pointer
对于每一种类型,提供了五类原子操作:
-
LoadXXX(addr)
: 原子性的获取*addr
的值,等价于:return *addr
-
StoreXXX(addr, val)
: 原子性的将val
的值保存到*addr
,等价于:addr = val
-
AddXXX(addr, delta)
: 原子性的将delta
的值添加到*addr
并返回新值(unsafe.Pointer
不支持),等价于:*addr += delta return *addr
-
SwapXXX(addr, new) old
: 原子性的将new
的值保存到*addr
并返回旧值,等价于:old = *addr *addr = new return old
-
CompareAndSwapXXX(addr, old, new) bool
: 原子性的比较*addr
和old
,如果相同则将new
赋值给*addr
并返回true
,等价于:if *addr == old { *addr = new return true } return false
因此第一部分的案例可以修改如下,即可通过
参考:https://juejin.cn/post/6844904053042839560
参考:https://blog.csdn.net/elihe2011/article/details/109157797
参考:https://segmentfault.com/a/1190000040545690
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。