Go并发系列:4原子操作-4.2 原子操作方法

137 阅读3分钟

4.2 原子操作方法

在并发编程中,原子操作方法提供了一种高效的方式来保证对共享数据的安全访问和修改。Go 语言的 sync/atomic 包提供了一组函数,支持对整数、布尔值和指针等基本数据类型的原子操作。这些函数能够在多 goroutine 并发操作时确保数据的一致性和正确性。下面我们详细介绍 Go 语言中常用的原子操作方法及其使用示例。

4.2.1 常用的原子操作方法

以下是 sync/atomic 包中常用的原子操作方法:

  • Add:执行加法操作。

    • atomic.AddInt32:对 int32 类型变量执行加法操作。
    • atomic.AddInt64:对 int64 类型变量执行加法操作。
    • atomic.AddUint32:对 uint32 类型变量执行加法操作。
    • atomic.AddUint64:对 uint64 类型变量执行加法操作。
  • Load:原子地读取变量的值。

    • atomic.LoadInt32:读取 int32 类型变量的值。
    • atomic.LoadInt64:读取 int64 类型变量的值。
    • atomic.LoadUint32:读取 uint32 类型变量的值。
    • atomic.LoadUint64:读取 uint64 类型变量的值。
    • atomic.LoadPointer:读取 unsafe.Pointer 类型变量的值。
  • Store:原子地设置变量的值。

    • atomic.StoreInt32:设置 int32 类型变量的值。
    • atomic.StoreInt64:设置 int64 类型变量的值。
    • atomic.StoreUint32:设置 uint32 类型变量的值。
    • atomic.StoreUint64:设置 uint64 类型变量的值。
    • atomic.StorePointer:设置 unsafe.Pointer 类型变量的值。
  • Swap:原子地交换变量的值。

    • atomic.SwapInt32:交换 int32 类型变量的值。
    • atomic.SwapInt64:交换 int64 类型变量的值。
    • atomic.SwapUint32:交换 uint32 类型变量的值。
    • atomic.SwapUint64:交换 uint64 类型变量的值。
    • atomic.SwapPointer:交换 unsafe.Pointer 类型变量的值。
  • CompareAndSwap:原子地比较并交换变量的值。

    • atomic.CompareAndSwapInt32:比较并交换 int32 类型变量的值。
    • atomic.CompareAndSwapInt64:比较并交换 int64 类型变量的值。
    • atomic.CompareAndSwapUint32:比较并交换 uint32 类型变量的值。
    • atomic.CompareAndSwapUint64:比较并交换 uint64 类型变量的值。
    • atomic.CompareAndSwapPointer:比较并交换 unsafe.Pointer 类型变量的值。

4.2.2 使用示例

以下是使用 sync/atomic 包进行原子操作的示例代码:

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var counter int32 = 0
    var wg sync.WaitGroup

    // 示例:Add 操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 1000; j++ {
                atomic.AddInt32(&counter, 1)
            }
        }()
    }

    wg.Wait()
    fmt.Printf("Final counter value after Add: %d\n", counter)

    // 示例:Load 操作
    value := atomic.LoadInt32(&counter)
    fmt.Printf("Loaded counter value: %d\n", value)

    // 示例:Store 操作
    atomic.StoreInt32(&counter, 42)
    fmt.Printf("Counter value after Store: %d\n", counter)

    // 示例:Swap 操作
    oldValue := atomic.SwapInt32(&counter, 100)
    fmt.Printf("Old counter value after Swap: %d, new value: %d\n", oldValue, counter)

    // 示例:CompareAndSwap 操作
    swapped := atomic.CompareAndSwapInt32(&counter, 100, 200)
    fmt.Printf("CompareAndSwap result: %v, counter value: %d\n", swapped, counter)
}

4.2.3 原子操作的应用场景

原子操作适用于多种并发编程场景:

  1. 计数器:在多线程环境下安全地递增或递减计数器。
  2. 标志位:安全地设置和读取标志位。
  3. 引用计数:管理资源的引用计数,例如内存或文件句柄。
  4. 锁实现:实现简单的自旋锁。
  5. 共享数据更新:安全地更新共享数据。

结论

原子操作方法为并发编程提供了高效的同步机制,确保了共享数据在多线程环境下的安全访问和修改。通过使用 sync/atomic 包提供的各种原子操作函数,开发者可以在不使用锁的情况下实现并发安全的数据操作。在接下来的章节中,我们将继续探讨其他同步原语和并发编程技巧,帮助您更好地掌握 Go 的并发编程。