首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang 语言标准库 sync/atomic 包原子操作

Golang 语言标准库 sync/atomic 包原子操作

作者头像
frank.
发布2020-12-22 14:35:08
6420
发布2020-12-22 14:35:08
举报

01

介绍

我们已经介绍过 Mutex、RWMutex 等并发原语操作,如果您还没有阅读,请查看文末「推荐阅读」列表。

本文我们介绍 sync/atomic 包提供的原子操作的方法,相比并发原语操作,使用原子操作会更轻量。

我们知道,相同代码在不同 CPU 架构中编译的结果可能不同,sync/atomic 包提供的原子操作的方法帮我们解决了这个问题,所以如果您想保证原子操作,一定要使用 sync/atomic 包提供的原子操作的方法。

02

方法

因为 Go (1.15)目前还没有支持泛型,所以每个 sync/atomic 包提供的原子操作的方法,都包含不同类型的同名方法。但是不同类型的同名方法除了类型不同,使用方法是相同的。

细心的读者可能发现,为什么 Add 方法只支持 5 种类型,其它方法都支持 6 种类型,因为在 Go 语言中,指针是不能运算的,Add 方法是修改操作,其它方法否是存取操作。限于篇幅,我们重点介绍 Add 方法。

Add 方法:

func AddInt32(addr *int32, delta int32) (new int32)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr

Add 方法的功能就是给参数 1 的地址中的值,加上参数 2 的 delta 的值。如果类型是有符号类型,参数 2 可以是负值,用于原子减法运算;如果类型是无符号类型,参数 2 不可以是负值,此时分两种情况:

  • 情况 1 是参数 2 是一个无符号类型的变量 v,-v 在 GO 语言中是合法的。
  • 情况 2 是参数 2 是一个正整数常量 c,-c 在 Go 语言中是非法的,此时我们可以使用 ^T(c-1) 作为参数 2 的值。

如果 ^T(c-1) 中的 c 是一个类型确定的值,可以简化为 ^(c-1)。

示例代码:

func main() {
  // 多个 goroutine 的原子计数器
  var counter uint64
  var wg sync.WaitGroup
  for i := 0; i < 50; i++ {
    wg.Add(1)
    go func() {
      for j := 0; j < 100000; j++ {
        // 相加
        atomic.AddUint64(&counter, 1)
      }
      wg.Done()
    }()
  }
  wg.Wait()
  fmt.Println("counter:", counter)
}

如果您感兴趣的话,可以将示例代码中第 10 行代码替换为 counter++,运行程序并查看运行结果是否会有不同。

Swap 方法:

func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)

Swap 方法的功能是原值替换为新值,并返回原值。

CompareAndSwap 方法:

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

CompareAndSwap 方法的功能是比较原值,符合条件则将原值替换为新值。

Load 方法:

func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)

Load 方法的功能是取值。

Store 方法:

func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

Store 方法的功能是存值。

03

value 类型

细心的读者可能已经发现,上述 5 种方法,都是针对特定类型的方法。sync/atomic 还有一个 value 类型,它有两个方法,分别是 Load 和 Store,用于读取和存储任意类型的值,它没有 Add 和 Swap。

func (v *Value) Load() (x interface{})
func (v *Value) Store(x interface{})

Load 方法的返回结果类型和 Store 方法的参数类型都是空接口类型 interface{},所以 Store 方法的参数可以接收任意类型的值。

需要注意的是,*Value 类型的 v,只要调用过一次 Store 方法,那么传入此方法的后续参数类型必须和之前传入参数的类型一致,否则会导致 panic。

04

总结

本文主要介绍了 sync/atomic 包的原子操作的方法的基本使用,其中重点介绍了 Add 方法和 Value 类型的方法,读者朋友可以根据实际场景选用最合适的方法。


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档