Golang Go语言中 32 位及以上的机器上,atomic.LoadInt32/WriteInt32 是否毫无意义?
我能查到的,原子操作的作用有两点:
1.保证操作要么执行完成,要么未执行,不会出现中间状态
2.保证读取的是内存上的最新值,而非缓存在寄存器上的值
众所周知,32 位及以上的机器对内存的读/写操作的最小单位是 32bit ,也就是说对一个 int32 进行读写,本身即可满足第一条要求。
至于第二条,可以分两种情况:
1. 如果不采用其他方如 mutex 来保证读取操作发生在写入操作后,那么读取操作本身就既可能读到新值也可能读到旧值,因此不在意读的是内存还是寄存器
2. 如果用了 mutex ,那么 mutex 可以保证读操作在写操作后,因此读到的值必然是已经被修改过的值,而非缓存值
因此,可否认为在 32 位及以上的机器上使用 atomic 包来单纯的读/写 int32 毫无意义,只有 cas 操作由于涉及到一次读和一次写才有用处?
Golang Go语言中 32 位及以上的机器上,atomic.LoadInt32/WriteInt32 是否毫无意义?
更多关于Golang Go语言中 32 位及以上的机器上,atomic.LoadInt32/WriteInt32 是否毫无意义?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在 x86_64 架构上:
变量地址对齐的情况下,atomic read/write 确实就是一条 mov 指令
但是 atomic 包应该还会额外提供插入 mfence 来阻止指令 store-load 重排的问题
更多关于Golang Go语言中 32 位及以上的机器上,atomic.LoadInt32/WriteInt32 是否毫无意义?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
请搜 memory order
“也就是说对一个 int32 进行读写,本身即可满足第一条要求”
否的,要考虑对齐情况
给你补充下,go 我不太清楚,Linux 内核的原子变量的 api 还有个功能就是防止操作被编译器优化。
假设一个循环(无内存屏障,同步原语情况下)里有 a = b ,编译器有可能把这句优化到循环之外,因为编译器的角度这句话运行一次和运行多次无异,但是我们知道 b 是被多个执行流共享的变量,所以需要使用 api 防止操作被优化。
内存可见性, 指令重排都不能保证
在 x86_64 上 atomic.LoadInt32 实际源码就是直接取数据,对应源码如下:<br>func Load(ptr *uint32) uint32 {<br> return *ptr<br>}<br>
而 WriteInt32 有所不同,其中使用了 XCHGL ,这个指令有 LOCK 指令前缀的效果,可以保证可见性。<br>TEXT ·Store(SB), NOSPLIT, $0-12<br> MOVQ ptr+0(FP), BX<br> MOVL val+8(FP), AX<br> XCHGL AX, 0(BX)<br> RET<br>
因此在 x86_64 平台上不使用 atomic 包也能保证原子性,但是保证不了可见性,如果程序中对可见性没有要求,以我的认知我觉得可以不使用 atomic 包,话虽如此实际工作中并发场景中该加还是加吧,说不准认知之外还有什么别的坑呢?
btw ,多年前我对这个问题也有过一段时间疑问与探索,整理了一篇博客: https://blog.fanscore.cn/a/34/ 可以参考一下。
请参考 c++ 的 memory order: https://zh.cppreference.com/w/cpp/atomic/memory_order
这堆内存序的说明可以帮助你理解为什么要有专门的原子操作函数
官方文档有说明,go 内存模型符合 drf-sc ,所以写程序符合 data race free 的约定就没啥问题
在Golang(Go语言)中,atomic.LoadInt32
和 atomic.WriteInt32
函数的作用和意义并不因运行环境是32位还是64位机器而有所减损,它们在设计上是为了在多线程环境下安全地读写32位整数。
首先,需要明确的是,在多线程编程中,即便是32位系统,对共享变量的读写也需要考虑并发安全问题。直接使用普通的读写操作(如 * = value
或 value = *
)可能会因为竞态条件导致数据不一致或其他未定义行为。
atomic.LoadInt32
和 atomic.WriteInt32
通过底层硬件指令(如CAS操作)实现了对32位整数的原子读写,保证了操作的不可分割性和线程安全性。这意味着在多核处理器上,这些操作不会被中断,从而避免了竞态条件。
此外,即使在现代64位系统上,32位整数的原子操作依然有其应用场景。例如,某些数据结构可能只需要32位整数来表示状态或计数,而使用原子操作可以确保对这些状态的读写是线程安全的。
因此,无论是在32位还是64位机器上,atomic.LoadInt32
和 atomic.WriteInt32
都是非常有用的,它们提供了在多线程环境下安全地读写32位整数的机制,确保了程序的正确性和稳定性。所以,说它们“毫无意义”是不准确的。