MST

星途 面试题库

面试题:Go语言中原子操作的常见场景及示例

请举例说明在Go语言中,哪些并发场景下会用到原子操作,并给出对应的代码示例,解释原子操作如何保证该场景下的并发安全。
15.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

在Go语言中,以下是一些会用到原子操作的并发场景:

计数器

当多个goroutine需要对一个计数器进行增减操作时,如果不使用原子操作,可能会出现竞态条件。

示例代码:

package main

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

func main() {
    var counter int64
    var wg sync.WaitGroup
    numGoroutines := 1000

    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            atomic.AddInt64(&counter, 1)
        }()
    }

    wg.Wait()
    fmt.Println("Final counter value:", counter)
}

解释:在上述代码中,多个goroutine同时对counter进行增加操作。使用atomic.AddInt64函数,它以原子方式将给定值加到counter上,保证每次操作都是完整且不可分割的,从而避免了竞态条件,确保并发安全。

标志位的设置与检查

例如在多个goroutine中需要设置或检查一个标志位,以表明某个操作是否完成。

示例代码:

package main

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

func main() {
    var flag int32
    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        time.Sleep(2 * time.Second)
        atomic.StoreInt32(&flag, 1)
    }()

    go func() {
        defer wg.Done()
        for {
            if atomic.LoadInt32(&flag) == 1 {
                fmt.Println("Flag is set.")
                break
            }
            time.Sleep(500 * time.Millisecond)
        }
    }()

    wg.Wait()
}

解释:第一个goroutine在2秒后设置标志位flag,第二个goroutine不断检查flagatomic.StoreInt32atomic.LoadInt32函数分别用于原子地设置和加载flag的值,确保在并发环境下,设置和检查操作的正确性,避免竞态。