• 【Go】原子操作atomic.Value的使用


    概述
    Go的sync/atomic包提供了原子操作,支持的数据类型包括:

    int32, int64, uint32, uint64, uintptr, unsafe.Pointer
    1
    若需要扩大原子操作的适用范围,可以使用atomic包中的Value。利用它可以实现对任意值进行原子得存储与加载。

    使用注意点
    atomic.Value只有两个指针方法:Store、Load。使用时需要遵循两个原则:1.不能存储nil;2.存储第一个值后,就只能存储这个类型的值。
    看一下atom.Value的实现可以发现一个内部的结构ifaceWords,使用unsafe.Pointer存储数据类型及内容得指针

    type ifaceWords struct {
    typ unsafe.Pointer
    data unsafe.Pointer
    }
    1
    2
    3
    4
    在使用Store进行存储时,首先判断待存储值是否为nil,若为nil会直接panic。之后会读取typ,若为nil则会将待存储值得类型、值的指针分别对typ、data进行赋值;若不为nil,则会判断待存储值的类型是否与既有的typ一致,不一致也会引起panic,一致的话则是将新值的指针赋予data。
    为了防止在使用是意外出现panic,所以可以考虑在外部先进行合法性校验。

    引用类型带来的坑点
    因为atom.Value内部实际上维护的是存储值的指针,而这个指针因为不对外暴露,所以认为是并发安全的。然而如果尝试用它来存储引用类型,维护的就是这个引用类型的指针,则不能保证实际的数据是并发安全的。举个例子:
    uint32是值类型,切片[]uint32是引用类型,我们使用这样两个函数来尝试修改值。

    //值类型
    func atomic_value(a uint32) {
    var v atomic.Value
    v.Store(a)
    a = 666
    fmt.Println(a)
    fmt.Println(v.Load())
    }

    // 引用类型
    func atomic_slice(s []uint32) {
    var v atomic.Value
    v.Store(s)
    s[0] = 666
    fmt.Println(s)
    fmt.Println(v.Load())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    然后我们分别调用这两个函数

    func TestAtomValue(t *testing.T) {
    atomic_value(1)
    atomic_slice([]uint32{1,2,3})
    }
    1
    2
    3
    4
    最后它的输出是这样的:

    可以看到我们使用Store存入uint32的值a后,我们无论怎么在外部修改a,使用Load都可以获取到我们Store的值。
    然而我们若使用Store存入引用类型的切片,我们在外部修改值,Load出来的值也会收到影响。这是因为对于一个引用类型,我们实际上只是Store了一个指针,只是对一个指针的原子操作,而这个指针实际指向的地址的值,并不在atomic.Value的维护下,所以并不是并发安全的。

    总结
    1.atomic.Value可以实现对自定义类型的原子操作
    2.不能存入nil
    3.对于同一个atomic.Value不能存入类型不同的值
    4.最好不要使用atomic.Value存储引用类型的值,可能导致数据不是并发安全的
    ————————————————
    版权声明:本文为CSDN博主「雨儿酱在鹿上」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/q895431756/article/details/111063656

  • 相关阅读:
    中国行业应用软件领域恶性循环的原因是什么?【转载】
    UED之开新窗口
      关于周华健,我觉得有那么几个时期:转
    投影
    undo自动调优介绍
    (原)Oracle事务与Undo段的分配过程
    数据所在的数据块实验
    Oracle 检查点队列与增量检查点
    GC Buffer Busy Waits处理
    如何找出Oracle instance中当前打开游标open cursor的总数?
  • 原文地址:https://www.cnblogs.com/ExMan/p/14536763.html
Copyright © 2020-2023  润新知