• 一文搞明白golang底层原子级内存操作 的使用(sync atomic包)


            在我们的程序开发中,对于并发的处理一直都是一件很头疼的事情(Rust这种天生无并发困扰的语言除外), 在go语言中,官方也给我们提供了底层的原子级内存操作,这对于同步算法的实现是非常有用的。

    atomic包使用结论

            由于这个包里面定义的一堆函数官方都不推荐使用,所以这个包里面的函数仅作为参考。我们主要搞明白类型定义和使用即可。 这个atomic包里面的类型定义看似一大推,其实归纳起来就8种,分别是 :Bool, Int32/64, Uint32/64, Pointer, Uintptr, Value ;  还有他们的方法,基本都一样,即 最多也就以下这5个方法:

    1. Add对原子数增加的一个增量数据;
    2. Store将数据存储到原子中,当这个Store被调用的时候,他的数据是不允许被拷贝的;
    3. Load从原子中加载对应的数据;
    4. Swap使用新的数据替换就的数据;
    5. CompareAndSwap比较和交换数据。

    只要你搞明白了上面的5个方法的使用,这个atomic包的使用也就明白了。

    原子级内存操作使用示例

    以下我们就以 atomic.Int64 类型的使用为例,来说明如何使用, 其他的类型使用都是一样的,不同的类型方法多少而已,只要有的方法实用都是一样的。

    直接上测试用例, 看明白这个测试用例也就明白了, 里面都有详细的注释说明。

    1. import (
    2. "sync/atomic"
    3. "testing"
    4. )
    5. // sync atomic数据类型操作使用示例, 单元测试用例
    6. // 其他的另外7种atomic类型的使用和这个都类似,不在赘述!
    7. // @author tekintian
    8. // @see https://pkg.go.dev/sync/atomic
    9. func TestAtomicType(t *testing.T) {
    10. // int64类型的原子数,
    11. // 他对于的类型声明是 type Int64 struct {// contains filtered or unexported fields}
    12. // 这个地方我们只需要定义就可以,不需要初始化. 其他几种类型也是一样
    13. var an1 atomic.Int64
    14. // 定义了变量后,我们就可以操作他对应的5个方法了
    15. an1.Store(10) // 把 10存入这个原子变量
    16. an1.Add(1) // 增量加1 类似于 10+1
    17. ret := an1.Load() // Load取出结果,这里ret为 11
    18. if ret != 11 {
    19. t.Fatalf("test failed expected 10, got %d", ret)
    20. } else {
    21. t.Logf("store 10 add 1 ok, got %d", ret)
    22. }
    23. an1.Swap(100) // 使用100对原子数据进行交换, 交换后的结果为100
    24. if an1.Load() != 100 {
    25. t.Fatalf("test failed expected to be 100, got %v", an1.Load())
    26. } else {
    27. t.Logf("an1.Swap(100) ok, got %d", an1.Load())
    28. }
    29. // 比较并交换,这里会拿第一个参数的值和原子数进行比较,
    30. // 如果第一个参数的值和原子数相等,就会拿第二个参数的值对对原子数进行交换, 否则返回false,不进行交换
    31. swapped := an1.CompareAndSwap(10, 200) // 第一个参数 10 和当前原子数100比较,不会被交换
    32. if !swapped {
    33. t.Logf("atomic data not swapped, now data is %v, but param1 for compare is 10", an1.Load())
    34. } else {
    35. t.Fatalf("an1.CompareAndSwap(10, 200) fail, expected 100 got %d", an1.Load())
    36. }
    37. if an1.CompareAndSwap(100, 200) { // 会被交换
    38. // 现在原子的数据应该是 200
    39. t.Logf("atomic data swapped, now data is %v ", an1.Load())
    40. } else {
    41. t.Fatalf("atomic data swapped failed: %v", an1.Load())
    42. }
    43. /*// 单元测试结果 下面的参数 -v 表示输出测试日志 即 使用t.Log输出的内容
    44. ✗ go test -run=^TestAtomicType$ -v
    45. === RUN TestAtomicType
    46. type_val_test.go:43: store 10 add 1 ok, got 11
    47. type_val_test.go:49: an1.Swap(100) ok, got 100
    48. type_val_test.go:55: atomic data not swapped, now data is 100, but param1 for compare is 10
    49. type_val_test.go:62: atomic data swapped, now data is 200
    50. --- PASS: TestAtomicType (0.00s)
    51. PASS
    52. ok atomic_demo 0.356s
    53. */
    54. }

    另外2个官方示例就不贴了,原理都一样, 大家有兴趣的话可以自己去瞄瞄 pkg.go.dev/sync/atomic#example-Value-ReadMostly

    怎么样,这个看似很神秘,其实也很简单的底层原子级内存操作是不是明白了?

    atomic类型定义参考

    1. type Bool
    2. func (x *Bool) CompareAndSwap(old, new bool) (swapped bool)
    3. func (x *Bool) Load() bool
    4. func (x *Bool) Store(val bool)
    5. func (x *Bool) Swap(new bool) (old bool)
    6. type Int32
    7. func (x *Int32) Add(delta int32) (new int32)
    8. func (x *Int32) CompareAndSwap(old, new int32) (swapped bool)
    9. func (x *Int32) Load() int32
    10. func (x *Int32) Store(val int32)
    11. func (x *Int32) Swap(new int32) (old int32)
    12. type Int64
    13. func (x *Int64) Add(delta int64) (new int64)
    14. func (x *Int64) CompareAndSwap(old, new int64) (swapped bool)
    15. func (x *Int64) Load() int64
    16. func (x *Int64) Store(val int64)
    17. func (x *Int64) Swap(new int64) (old int64)
    18. type Pointer
    19. func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool)
    20. func (x *Pointer[T]) Load() *T
    21. func (x *Pointer[T]) Store(val *T)
    22. func (x *Pointer[T]) Swap(new *T) (old *T)
    23. type Uint32
    24. func (x *Uint32) Add(delta uint32) (new uint32)
    25. func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool)
    26. func (x *Uint32) Load() uint32
    27. func (x *Uint32) Store(val uint32)
    28. func (x *Uint32) Swap(new uint32) (old uint32)
    29. type Uint64
    30. func (x *Uint64) Add(delta uint64) (new uint64)
    31. func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool)
    32. func (x *Uint64) Load() uint64
    33. func (x *Uint64) Store(val uint64)
    34. func (x *Uint64) Swap(new uint64) (old uint64)
    35. type Uintptr
    36. func (x *Uintptr) Add(delta uintptr) (new uintptr)
    37. func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool)
    38. func (x *Uintptr) Load() uintptr
    39. func (x *Uintptr) Store(val uintptr)
    40. func (x *Uintptr) Swap(new uintptr) (old uintptr)
    41. type Value
    42. func (v *Value) CompareAndSwap(old, new any) (swapped bool)
    43. func (v *Value) Load() (val any)
    44. func (v *Value) Store(val any)
    45. func (v *Value) Swap(new any) (old any)

     atomic函数的定义参考

    这个看上去一大堆,其实下面这些个函数的定义官方都不建议使用! 在手册中你都能看到这样一句话“Consider using the more ergonomic and less error-prone xxx instead.”  都建议你使用更符合人体工程学的且不容易出错的 xxx 方法代替

    1. func AddInt32(addr *int32, delta int32) (new int32)
    2. func AddInt64(addr *int64, delta int64) (new int64)
    3. func AddUint32(addr *uint32, delta uint32) (new uint32)
    4. func AddUint64(addr *uint64, delta uint64) (new uint64)
    5. func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
    6. func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
    7. func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
    8. func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
    9. func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
    10. func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
    11. func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
    12. func LoadInt32(addr *int32) (val int32)
    13. func LoadInt64(addr *int64) (val int64)
    14. func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
    15. func LoadUint32(addr *uint32) (val uint32)
    16. func LoadUint64(addr *uint64) (val uint64)
    17. func LoadUintptr(addr *uintptr) (val uintptr)
    18. func StoreInt32(addr *int32, val int32)
    19. func StoreInt64(addr *int64, val int64)
    20. func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
    21. func StoreUint32(addr *uint32, val uint32)
    22. func StoreUint64(addr *uint64, val uint64)
    23. func StoreUintptr(addr *uintptr, val uintptr)
    24. func SwapInt32(addr *int32, new int32) (old int32)
    25. func SwapInt64(addr *int64, new int64) (old int64)
    26. func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
    27. func SwapUint32(addr *uint32, new uint32) (old uint32)
    28. func SwapUint64(addr *uint64, new uint64) (old uint64)
    29. func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)

    参考文档 atomic package - sync/atomic - Go Packages

  • 相关阅读:
    第22节-PhotoShop基础课程-模糊工具
    Excel宏(VBA)自动化标准流程代码
    centos7安装k8s 1.24.3版本 Error getting node“ err=“node \“master01\“ not found
    MQTT协议消息代理服务公网远程连接
    element中Notification组件(this.$notify)自定义样式
    Python中不为人知的四个特性
    MongoDB聚合运算符:$lte
    XX市消防救援指挥中心实战指挥平台多链路聚合解决方案实例
    springboot+vue+elementUI 会员制医疗预约服务管理信息系统-#毕业设计
    中小企业选择ERP系统时应关注的10个关键功能
  • 原文地址:https://blog.csdn.net/tekin_cn/article/details/139421999