LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

    • 并发编程

      • GMP并发模型
      • 锁相关
      • groutine并发相关
      • go如何实现原子操作
      • 内存管理

      • 数组和切片的区别
      • new和make
      • go defer
      • context
      • channel
      • go map
      • interface
      • 对象系统
      • rune 类型
      • 字符串拼接的几种方式
    • cplus

    • leetcode

    • 存储技术

    • 分布式系统

    • 计算机网络

    • Linux操作系统

    • Redis

    • 其他

    • 笔记
    • golang
    • 并发编程
    lisheng
    2024-09-10
    目录

    go如何实现原子操作

    Go 语言通过 sync/atomic 包提供了一组用于原子操作的函数,这些函数能够在多线程环境中确保对共享变量的安全访问。原子操作是一种确保在执行某个操作时,不会被中断的操作,这在并发编程中至关重要。

    # 1. 原子操作的基础

    原子操作通过硬件级别的支持来实现,通常使用 CPU 提供的原子指令(如 compare-and-swap (CAS))来完成。在 Go 中,sync/atomic 包封装了这些底层的硬件指令,提供了一些常用的原子操作函数,适用于 int32、int64、uint32、uint64 和 uintptr 等类型的变量。

    # 2. 常见的原子操作函数

    sync/atomic 包中的函数大致可以分为以下几类:

    • 加法与减法

      • atomic.AddInt32(&val, delta int32) int32
      • atomic.AddInt64(&val, delta int64) int64
      • 这些函数用于对整数进行原子加法或减法操作。
    • 加载与存储

      • atomic.LoadInt32(&val int32) int32
      • atomic.LoadInt64(&val int64) int64
      • atomic.StoreInt32(&val *int32, new int32)
      • atomic.StoreInt64(&val *int64, new int64)
      • 加载和存储操作用于安全地读取或写入共享变量。
    • 交换操作

      • atomic.SwapInt32(&val int32, new int32) int32
      • atomic.SwapInt64(&val int64, new int64) int64
      • 交换操作会将一个新值写入变量,并返回变量的旧值。
    • 比较并交换(CAS)

      • atomic.CompareAndSwapInt32(&val *int32, old, new int32) bool
      • atomic.CompareAndSwapInt64(&val *int64, old, new int64) bool
      • 比较并交换操作会在 val 的当前值等于 old 时,将其更新为 new,否则不做任何操作。

    # 3. 原子操作的实现原理

    sync/atomic 包中的操作是通过底层 CPU 的原子指令实现的,确保这些操作在多处理器环境中是原子的,不会被中断。

    • CompareAndSwap (CAS): 这是最基础的原子操作,通过比较当前值与期望值,如果相等则更新为新值。这一操作在硬件上通过指令(如 LOCK CMPXCHG)实现。

    • 内存屏障: 为了确保内存操作的顺序性,Go 的原子操作通常伴随着内存屏障(Memory Barrier),这阻止了编译器或 CPU 对指令的重新排序,从而保证在多核环境下的内存一致性。

    # 4. 原子操作的使用场景

    • 计数器: 可以使用 atomic.AddInt64 来实现线程安全的计数器,而不需要引入复杂的锁机制。

    • 状态机: 使用 atomic.CompareAndSwap 可以实现线程安全的状态机,确保状态转换的正确性。

    • 指针交换: atomic.SwapPointer 可以用于实现高效的指针交换操作,这在实现无锁数据结构时非常有用。

    # 5. 与互斥锁的对比

    原子操作通常比互斥锁更轻量,因为它们不会导致上下文切换或线程调度。然而,原子操作只适用于单一变量的读写,对于更复杂的数据结构,仍需要使用互斥锁来保护。

    # 6. 实际代码示例

    以下是一个使用原子操作实现线程安全计数器的示例:

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

    在这个例子中,atomic.AddInt64 保证了对 counter 的加操作是线程安全的,最终的计数值将是 1000。

    # 总结

    Go 语言通过 sync/atomic 包提供了原子操作的支持,这些操作可以在无锁的情况下安全地进行并发访问,是实现高性能并发程序的重要工具

    编辑 (opens new window)
    上次更新: 2024/09/13, 11:59:12
    groutine并发相关
    gc整体过程

    ← groutine并发相关 gc整体过程→

    最近更新
    01
    ceph分布式存储-对象存储(RGW)搭建
    10-27
    02
    ceph分布式存储-集群客户端连接
    10-27
    03
    ceph分布式存储-管理crushmap
    10-27
    更多文章>
    Theme by Vdoing
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式