在并发操作中,原子操作是非常现实的问题,典型的就是多个 CPU 对同一个内存的值进行操作,如 i++,很可能两次 i++,这个 i 只增加了一次,sync/atomic 就是为了解决这个问题的。具体的实现在不同的操作系统的实现是不同的, Intel 的 CPU 架构机器上,主要是使用总线锁的方式实现的。 在 AMD 的 CPU 架构机器上就是使用 MESI 一致性协议的方式来保证原子操作。

Atomic 提供了对简单类型进行操作,这些类型包括 int32, int64, uint32, uint64, uintptr, unsafe.Pointer 共6个。这些函数的原子操作共有5种:增或减,比较并交换、载入、存储和交换

如,当我们不使用原子操作的时候:

package main

import (
   strconv
)

var num int64

func main() {
   num = 0
   ch := make(chan string)

   for i := 0; i < 10000; i++ {
      go add(ch, i)
   }

   for i := 0; i < 10000; i++ {
      <-ch
   }

   println(num)
}

func add(ch chan string, i int)  {
   num++
   ch <- (add + strconv.Itoa(i))
}

多次运行,最终得到的 num 值是不同的:

width=600

按照代码逻辑应该是需要为 10000 则是符合预期的,更改使用原子操作时,将 num++ 改为 atomic.AddInt64(&num, 1)

package main

import (
   strconv
   sync/atomic
)

var num int64

func main() {
   num = 0
   ch := make(chan string)

   for i := 0; i < 10000; i++ {
      go add(ch, i)
   }

   for i := 0; i < 10000; i++ {
      <-ch
   }

   println(num)
}

func add(ch chan string, i int)  {
   atomic.AddInt64(&num, 1)
   ch <- (add + strconv.Itoa(i))
}

这个时候是符合预期的

 

图片[2]-Golang 学习笔记:Golang 的 sync/atomic 原子操作-技术鸭(jishuya.cn)

 

© 版权声明
评论 抢沙发

请登录后发表评论