如何使用atomic原子操作进行Goroutine的同步?

使用 Go 语言中的 atomic 包提供的原子操作可以实现对变量的原子访问,从而避免在多个 Goroutine 中访问同一个变量导致的竞争条件问题。

atomic 包提供了多个函数,包括 Add、CompareAndSwap、Load、Store 等,用于对变量进行原子操作。例如,使用 atomic.AddInt32 函数可以原子地将 int32 类型的变量增加指定的值。示例如下:

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    var num int32 = 0
    var cnt int32 = 100

    // 创建10个Goroutine进行加法操作
    for i := 0; i < 10; i++ {
        go func() {
            for j := 0; j < 10; j++ {
                atomic.AddInt32(&num, 1)
            }
            atomic.AddInt32(&cnt, -1)
        }()
    }

    // 等待所有Goroutine执行完毕
    for cnt > 0 {
    }

    fmt.Println(num)
}

在上面的示例中,我们创建了10个 Goroutine,每个 Goroutine 执行10次 atomic.AddInt32 操作,将 num 变量增加1,然后执行 atomic.AddInt32(&cnt, -1) 将 cnt 变量减1,表示这个 Goroutine 执行完毕。

为了等待所有 Goroutine 执行完毕,我们使用了一个 cnt 变量来记录还有多少个 Goroutine 没有执行完毕。在主 Goroutine 中,我们不断地循环判断 cnt 是否为0,如果为0则说明所有 Goroutine 都已经执行完毕,主 Goroutine 才会退出。

在使用 atomic 包时,需要注意以下几点:

1、原子操作只能用于基本类型和指针类型,不适用于复杂的数据类型。
2、原子操作可以用于同步读写操作,但是不能用于同步复合操作,例如读取和更新变量的值。
3、原子操作虽然可以避免锁竞争的问题,但是在高并发情况下,仍然会存在性能瓶颈。在实际应用中,应根据具体情况综合考虑使用锁和原子操作。