Russ Cox在去年的系列文章中,提到对atomic包的改变,并且开了一个issue供大家讨论,现在他提交的改变已经merge到master分支,Go 1.19就会包含这些改变。
你也可以使用gotip提前了解这些改变。
Russ Cox这次的提交只是对atomic补充了一些新的类型,这些类型是对基本类型(primitive type, 如bool、int32、int64、uint32、uint64、uintptr等)的一个包装,以便提供原子操作。
事实上,uber很早以前就提供类似的功能,也许你很早之前就使用过uber-go/atomic这个库。
Russ Cox实现的方式也类似,毕竟,这个为基本类型实现包装器的套路还是比较固定的,但是实现上略微有些不同。
- Russ Cox在标准库中的实现
- 嵌入
_ noCopy
,方便go vet等工具做data race检查 - 代码可以进行inline,提高性能
- 嵌入
- Uber的实现
- 嵌入
_ nocmp
,避免非原子比较 - 提供JSON Marshal/UnMarshal的功能,方便序列化
- 提供更多类型的包装器:
Duration
、String
、Time
、Float64
、Error
- 嵌入
我们在这篇文章中主要介绍Russ Cox的实现,毕竟这是Go标准库中的改动,我多少要对这些改动有些印象,在我们将来的代码开发,或者看别人的代码时做到心中有数。
这次改动增加了bool、int32、int64、uint32、uint64、uintptr、Value、unsafe.Pointer类型的包装器:Bool
、Int32
、Int64
、Uint32
、Uint64
、Uintptr
、Value
、Pointer
。
所有这些类型都包含下面四个方法:
- CompareAndSwap(old, new *T) (swapped bool) : 执行CAS操作
- Load() *T : 原子load对应的值
- Store(val *T) : 将值val原子存储
- Swap(new T) (old T): 原子保存新值,并返回老的值
针对不同的类型,可能还会有一些额外的方法,比如:
- Int32:
func (x *Int32) Add(delta int32) (new int32)
- Int64:
func (x *Int64) Add(delta int64) (new int64)
- Uint32:
func (x *Int64) Add(delta int64) (new int64)
- Uint64:
func (x *Uint64) Add(delta uint64) (new uint64)
- Uintptr:
func (x *Uintptr) Add(delta uintptr) (new uintptr)
同样,针对Uint32
、Uint64
类型,只有Add方法,如果想减去一个正值c,就得利用Add加上一个负值(-c),可是delta的类型都是uxxx类型,怎么办呢?使用^uint64(c-1)
或者^uint32(c-1)
。
这些包装器和方法其实对应的是atomic的相应的函数,所以使用起来没什么难度:
|
|
你可以查看go doc了解更详细的信息: sync/atomic