• Golang笔记|Atomic


    作用与优势

    atomic 包是 Go 语言中用于进行原子操作的标准库。原子操作是一种在多线程并发编程中用来确保数据安全的机制,它可以防止多个线程同时访问同一个资源而导致的数据竞争问题。

    atomic 包中的函数是原子操作,它们在执行时不会被中断,从而确保操作的不可分割性。这对于执行复杂的操作,如比较并交换,是非常重要的。

    在某些情况下,使用互斥锁来保护共享变量可能会导致额外的锁开销和上下文切换,从而影响性能。atomic 包的原子操作不需要使用锁,可以减少这些开销。

    原子操作通常比互斥锁更高效,特别是在一些轻量级的操作中。在高并发的情况下,使用原子操作可以提高程序的性能。

    适用场景

    • 计数器和累加器: 当多个协程需要对一个共享的计数器或累加器进行增加、减少或重置操作时,使用 atomic
      包可以避免数据竞争,确保操作的原子性。
    • 标志位和状态切换: 当多个协程需要读取和修改某个共享的标志位或状态值时,使用 atomic
      包可以确保状态的正确性,避免并发修改导致的问题。
    • 缓存更新和缓存失效: 在缓存中存储的数据需要经常更新,且更新频率较高时,使用 atomic
      包可以保证更新操作的一致性,防止多个协程同时更新而导致的问题。
    • 资源池管理: 当多个协程需要从资源池中获取或释放资源时,使用 atomic 包可以保证资源池的状态正确,避免资源泄漏和竞争条件。
    • 引用计数: 在一些场景中,可能需要使用引用计数来管理共享资源的生命周期。使用 atomic 包可以确保引用计数的增加和减少操作是原子的。

    atomic包中的函数

    1.atomic.AddInt32, atomic.AddInt64, atomic.AddUint32, atomic.AddUint64:原子地将指定的值加到一个整数变量上。
    2.atomic.LoadInt32, atomic.LoadInt64, atomic.LoadUint32, atomic.LoadUint64:原子地读取一个整数变量的值。

    package main
    
    import (
       "fmt"
       "sync"
       "sync/atomic"
       "time"
    )
    
    func main() {
       var counter int64
       var wg sync.WaitGroup
    
       for i := 0; i < 10; i++ {
          wg.Add(1)
          go func() {
             for j := 0; j < 1000; j++ {
                atomic.AddInt64(&counter, 1)
             }
             wg.Done()
          }()
       }
    
       wg.Wait()
       fmt.Println("Counter value:", atomic.LoadInt64(&counter))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.atomic.StoreInt32, atomic.StoreInt64, atomic.StoreUint32, atomic.StoreUint64:原子地将指定的值写入到一个整数变量中。

    package main
    
    import (
       "fmt"
       "sync"
       "sync/atomic"
    )
    
    func main() {
       var value int64
       var wg sync.WaitGroup
    
       // 启动多个协程尝试存储值
       for i := 0; i < 10; i++ {
          wg.Add(1)
          go func(i int) {
             defer wg.Done()
    
             // 原子地将值设置为协程的编号
             atomic.StoreInt64(&value, int64(i))
             fmt.Printf("Goroutine %d: Stored value %d\n", i, i)
          }(i)
       }
    
       wg.Wait()
       fmt.Printf("Final value: %d\n", value)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4.atomic.CompareAndSwapInt32, atomic.CompareAndSwapInt64, atomic.CompareAndSwapUint32, atomic.CompareAndSwapUint64:比较并交换,比较当前addr地址里的值是不是old,如果不等于old,就返回false; 如果当前等于old,就把此地址的值替换成new值,返回true。

    package main
    
    import (
       "fmt"
       "sync"
       "sync/atomic"
    )
    
    func main() {
       var value int64 = 0
       var wg sync.WaitGroup
    
       // 启动多个协程尝试修改值
       for i := 0; i < 10; i++ {
          wg.Add(1)
          go func(i int) {
             defer wg.Done()
    
             // 尝试将值从旧值0修改为新值10
             swapped := atomic.CompareAndSwapInt64(&value, 0, 10)
             if swapped {
                fmt.Printf("Goroutine %d: Value swapped successfully\n", i)
             } else {
                fmt.Printf("Goroutine %d: Value was not swapped\n", i)
             }
          }(i)
       }
    
       wg.Wait()
       fmt.Printf("Final value: %d\n", value)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    6.atomic.SwapInt32, atomic.SwapInt64, atomic.SwapUint32, atomic.SwapUint64:原子地交换一个整数变量的值。如果不需要比较旧值,只是比较粗暴地替换的话,就可以使用Swap方法。

    
    ```go
    package main
    
    import (
       "fmt"
       "sync"
       "sync/atomic"
    )
    
    func main() {
       var value int64 = 5
       var wg sync.WaitGroup
    
       // 启动多个协程尝试交换值
       for i := 0; i < 10; i++ {
          wg.Add(1)
          go func(i int) {
             defer wg.Done()
    
             // 原子地交换值为新值10,并获取旧值
             oldValue := atomic.SwapInt64(&value, 10)
             fmt.Printf("Goroutine %d: Swapped value from %d to 10\n", i, oldValue)
          }(i)
       }
    
       wg.Wait()
       fmt.Printf("Final value: %d\n", value)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    
    
    • 1
  • 相关阅读:
    MySQL基础篇【第二篇】| 简单的查询、条件查询、排序查询
    3.8 Android eBPF HelloWorld调试(二)
    .NET Framework 工具 Resgen.exe(资源文件生成器)
    拆分代码 + 动态加载 + 预加载,减少首屏资源,提升首屏性能及应用体验
    SpringBoot+MyBatis Plus对Map中Date格式转换的处理
    渲染时间过长?这些参数设置学起来
    Pytorch模型训练实用教程学习笔记:二、模型的构建
    好物周刊#11:远程桌面软件
    深度、广度优先遍历(邻接表)
    查看锁定SAP账号的IP
  • 原文地址:https://blog.csdn.net/m0_73728511/article/details/133188256