在 Go 语言标准库中,sync/atomic包将底层硬件提供的原子操作封装成了 Go 的函数。但这些操作只支持几种基本数据类型,Go 语言在 1.4 版本的时候向sync/atomic包中添加了一个新的类型Value。此类型的值相当于一个容器,可以被用来“原子地”存储(Store)和加载(Load)任意类型的值。

原子操作

原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分。即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

原子性不可能由软件单独保证–必须需要硬件的支持,因此是和架构相关的。在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀”LOCK”,经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。

mutex操作系统实现,而atomic包中的原子操作则由底层硬件直接提供支持。因此原子操作可以在lock-free的情况下保证并发安全,并且它的性能也能做到随 CPU 个数的增多而线性扩展。

atomic提供的函数功能需要非常小心才能正确使用,除了在一些特殊的low-level程序中,同步最好使用通道或sync包的设施来完成。

atomic

1.4 版本前,sync/atomic包支持几种基本数据类型的原子操作,之后新增的atomic.Value类型,供一致类型值的原子加载和存储,支持任意类型的数据,内部的字段是一个interface{}类型。

1
2
3
4
5
6
7
8
// A Value provides an atomic load and store of a consistently typed value.
// The zero value for a Value returns nil from Load.
// Once Store has been called, a Value must not be copied.
//
// A Value must not be copied after first use.
type Value struct {
	v interface{}
}

实现解读

参见:Go 语言标准库中 atomic.Value 的前世今生

unsafe.Pointer

unsafe.Pointer 可以绕过 Go 语言类型系统的检查,完成任何类型与内建的 uintptr 类型之间的转化。unsafe.Pointer 可以实现四种其他类型不能的操作:

  • 任何类型的指针都可以转化为一个 unsafe.Pointer
  • 一个 unsafe.Pointer 可以转化成任何类型的指针
  • 一个 uintptr 可以转化成一个 unsafe.Pointer
  • 一个 unsafe.Pointer 可以转化成一个 uintptr

参考资源

  1. [atomic doc] https://pkg.go.dev/sync/atomic
  2. [Go 语言标准库中 atomic.Value 的前世今生] https://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651445490&idx=2&sn=03e00ae51a511392a9923f6d72349fc9&scene=21#wechat_redirect
  3. [原子操作] https://baike.baidu.com/item/%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C/1880992?fr=aladdin