MST

星途 面试题库

面试题:Go语言中原子操作如何保证并发安全

在Go语言中,阐述原子操作的常见类型(如加法、比较并交换等),并说明它们是怎样确保并发安全的,以`atomic.AddInt64`为例进行说明。
33.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

原子操作常见类型

  1. 加法操作:像 atomic.AddInt64 用于对 int64 类型的变量进行原子加法操作。在并发环境下,对数值类型变量的增减操作如果不使用原子操作,可能会出现竞态条件。
  2. 比较并交换(CAS)操作atomic.CompareAndSwapInt64 等。它先比较内存中的值与给定的旧值,如果相等则将其替换为新值。常用于实现无锁数据结构,例如链表的插入删除操作。
  3. 加载操作:如 atomic.LoadInt64,原子地加载 int64 类型变量的值,确保读取到的值是完整且未被并发修改影响的。
  4. 存储操作atomic.StoreInt64 原子地存储一个 int64 值到指定内存地址,保证存储操作的原子性,避免并发写冲突。

如何确保并发安全

Go语言的原子操作是通过底层硬件提供的原子指令来实现的。这些指令在硬件层面保证了操作的原子性,即操作要么完整执行,要么不执行,不会出现部分执行的情况。在多处理器系统中,这些指令还通过内存屏障(Memory Barrier)来保证内存一致性,确保不同处理器对共享内存的访问顺序符合预期,避免出现内存可见性问题。

atomic.AddInt64 为例说明

atomic.AddInt64 函数原型为 func AddInt64(addr *int64, delta int64) (new int64)。它原子地将 delta 添加到 addr 指向的 int64 变量上,并返回新的值。当多个 goroutine 同时调用 atomic.AddInt64 对同一个 int64 变量进行加法操作时,由于底层硬件的原子指令保证,不会出现一个 goroutine 读取了变量的值,另一个 goroutine 也读取了相同值并进行加法操作,导致结果错误的情况。每个加法操作都是原子的,确保了并发环境下对该变量加法操作的正确性和安全性。例如:

package main

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

func main() {
    var num int64
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            atomic.AddInt64(&num, 1)
        }()
    }
    wg.Wait()
    fmt.Println("Final value:", num)
}

在上述代码中,10 个 goroutine 并发地对 num 进行加法操作,使用 atomic.AddInt64 能确保最终 num 的值为 10,而不会因为并发问题导致值错误。