Golang:什么是原子读取用于?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang:什么是原子读取用于?相关的知识,希望对你有一定的参考价值。

在这里,我们有一个由Go by Example提供的案例来解释原子包。

https://gobyexample.com/atomic-counters

package main

import "fmt"
import "time"
import "sync/atomic"

func main() {

    var ops uint64

    for i := 0; i < 50; i++ {
        go func() {
            for {
                atomic.AddUint64(&ops, 1)

                time.Sleep(time.Millisecond)
            }
        }()
    }

    time.Sleep(time.Second)

    opsFinal := atomic.LoadUint64(&ops) // Can I replace it?
    fmt.Println("ops:", opsFinal)
}

对于atomic.AddUnit64来说,它很容易理解。

问题1

关于read操作,为什么有必要使用atomic.LoadUnit,而不是直接读这个计数器?

问题2

我可以用以下几行替换最后两行吗?

之前

    opsFinal := atomic.LoadUint64(&ops) // Can I replace it?
    fmt.Println("ops:", opsFinal)

    opsFinal := ops
    fmt.Println("ops:", opsFinal)

问题3

我们担心这种情况吗?

  1. CPU从内存加载数据
  2. CPU操纵数据
  3. 将数据写回内存。即使这一步很快,但仍需要时间。

当CPU执行step3时,另一个goroutine可能会从内存中读取不完整和脏的数据。那么使用atomic.LoadUint64可以避免这种问题吗?

参考

Are reads and writes for uint8 in golang atomic?

答案

有必要使用atomic.LoadUint64,因为不能保证:=操作符进行原子读取。

举个例子,考虑一个atomic.AddUint64实现如下的理论案例:

  1. 锁定。
  2. 读低32位。
  3. 读高32位。
  4. 将数字添加到低32位。
  5. 将第一个操作的执行添加到高32位。
  6. 写低32位。
  7. 写高32位。
  8. 解锁。

如果您不使用atomic.LoadUint64,您可能正在阅读第6步和第7步之间的中间结果。

在某些平台上(例如,没有本机支持64位整数运算的旧ARM处理器),它可以很好地以上述方式实现。

这同样适用于其他大小的整数/指针。确切的行为将取决于atomic包的实现以及运行该程序的CPU /内存体系结构。

以上是关于Golang:什么是原子读取用于?的主要内容,如果未能解决你的问题,请参考以下文章

golang代码片段(摘抄)

golang goroutine例子[golang并发代码片段]

c++11 多读取器/多写入器队列使用原子用于对象状态和永久递增索引

为啥保守光栅化无法为某些三角形调用片段着色器?

Golang写时复制是否是原子性的?

Golang内存模型