golang的atomic.LoadInt32/StoreInt32函数用法

CodingCode · · 3161 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

golang的atomic.LoadInt32/StoreInt32函数的用法

func LoadInt32(addr *int32) (val int32)
func StoreInt32(addr *int32, val int32)

直观上理解是把一个32位整数读出或者写入内存,保证读写的原子性。

为什么会有这样的需求?难道读写一个整数不是CPU指令级别能保证的原子操作吗?

我的理解,主要是基于两个原因:

  1. 兼容不同的计算机体系结构,从语言层面提供一个统一的函数。
    例如在现在32位/64位 RISC计算机体系结构下面,一条load/store指令就能读取32位/64位的数据,那么CPU指令级别确实能保证int32的读取完整性,也就LoadInt32/StoreInt32失去存在的必要性。
    但是不同的计算机体系结构,不一定能提供这种指令,举个例子部分老式CPU可能需要4条指令,一次读取一个字节,这种情况下就必须保证读写的原子性。
    还有啊在32位计算机体系结构下,读写一个64位的数据,必然需要两条指令,这时LoadInt64/StoreInt64就需要了。

  2. 提供读写的同步
    直接变量访问指令可能不会引起内存的读取,现在编译器都会做优化,可以把变量优化到寄存器,在代码运气期间对变量的读写直接访问寄存器,而不写入内存;这在同一个执行上下文是没有问题的,而如果需要跨线程访问,则永远看不到变量值的变化。下面一个例子说明这个问题:

package main

import (
    "fmt"
    "time"
)

var x int64 = 0x3333333333333333

func storeFunc() {
    for i := 0; ;i++ {
        if i % 2 == 0 {
            x = 0x1111111111111111
        } else {
            x = 0x2222222222222222
        }
        //time.Sleep(10 * time.Millisecond)
    }
}

func main() {
    go storeFunc()

    for {
        time.Sleep(10 * time.Millisecond)
        fmt.Printf("%x\n", x)
    }
}

这段代码:

  1. 定义一个全局变量x初始值为0x333333333333
  2. 定义一个goroutine,依次修改变量x的值为0x1111111111111111和0x2222222222222222
  3. 在main里面,不停的读取x的值,打印出来。
  4. 我们会看到打印出来的结果始终是:0x3333333333333333

也就是说storeFunc的代码没有被执行到;实际上是函数storeFunc的代码编译器做了优化,对x的赋值并没有写回到内容(可能就存储在寄存器中),而storeFunc和main是在两个goroutine里面运行的,他们并不共享CPU执行上下文,从而main读出的值永远是初始值。

如果我们在storeFunc的循环内加一个sleep间隔,让x的值能够被写入内容,那么main函数就会读出0x1111111111111111和0x2222222222222222的值。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:CodingCode

查看原文:golang的atomic.LoadInt32/StoreInt32函数用法

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3161 次点击  ∙  2 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传