Go atomic

Go atomic

一、介绍

atomic 是 Go 标准库中用于实现原子操作 的包,核心解决多 goroutine 并发下的变量竞态问题,比互斥锁(sync.Mutex)更轻量、性能更高。

二、原子操作

2.1 介绍

原子操作是指不可被中断的操作:要么完全执行完毕,要么完全不执行,中间不会被其他goroutine抢占。

2.2 底层原理

atomic.AddInt32(&x, 1) 是一步完成的,底层依赖 CPU 指令(如 LOCK XADD),通过锁定内存总线/ 缓存行,确保同一时刻只有一个CPU核心操作该内存地址,保证原子性,无竞态风险

三、适用场景

  • 轻量级的计数(eg 请求数、并发数统计)
  • 状态标记(eg 服务是否启动 任务是否完成)
  • 高性能的并发变量更新(替代互斥锁、减少上下文切换)

四、核心功能

atomic 主要操作基本数值类型int32/int64/uint32/uint64/uintptr)和指针类型 (Pointer)

ps :atomic 操作的对象是一个地址 ,需要传递地址

4.1 Add

对数值类型进行原子增减,给第一个参数地址中的值增加一个 delta 值

  • 变量必须是指针类型(因为要直接操作内存)
  • uint 类型加法:若要实现减法,需通过 atomic.AddUint32(&x, ^uint32(n-1))(利用补码),例如 atomic.AddUint32(&x, ^uint32(0)) 等价于 x--
go 复制代码
package main

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

func main() {
	var count int32 = 0 // 必须是int32/int64等原子支持的类型
	var wg sync.WaitGroup

	// 启动1000个goroutine,每个goroutine对count加1
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			// 原子加1:第一个参数是变量指针,第二个是增量(负数表示减)
			atomic.AddInt32(&count, 1)
		}()
	}

	wg.Wait()
	fmt.Println("最终计数:", count) // 输出 1000(无竞态)
}

4.2 加载操作(Load)

原子读取变量值,保证读取到的是最新值(避免编译器 / CPU 缓存导致的可见性问题)

4.3 存储操作(Store)

原子写入变量值,保证写入的原子性和可见性(其他 goroutine 能立即看到最新值)

替代普通赋值 x = 0,避免并发下的写覆盖问题

go 复制代码
package main

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

var status int32 = 1

func getStatus() {
	fmt.Println(atomic.LoadInt32(&status))
}

func stop() {
	atomic.StoreInt32(&status, 0)
}
func main() {
	go getStatus()
	go stop()
	time.Sleep(time.Second)
}
结果可能是1也可能是0

4.4 比较并交换(CAS)

go 复制代码
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)

方法逻辑:

  • 检查 addr 指向的变量值是否等于 old
  • 若相等,将其更新为 new,返回 true
  • 若不相等,不做修改,返回 false

CAS 是乐观锁思想,无阻塞但可能重试;

Go 中 sync/atomic 的 CAS 是硬件级别的支持,性能远高于互斥锁

go 复制代码
package main

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

var status int32 = 1

func incr() {
	for {
		old := atomic.LoadInt32(&status)
		new := old + 1
		// CAS操作:只有old等于当前值时才更新
		if atomic.CompareAndSwapInt32(&status, old, new) {
			fmt.Println("status:", atomic.LoadInt32(&status))
			break
		}
	}
}

func main() {
	go incr()
	time.Sleep(time.Second)
}

4.5 交换操作(Swap)

无条件原子替换变量值,返回旧值,eg: 强制重置计数器

4.5 atomic.Value

支持`任意类型的原子读写

ps :

  • atomic.Value 存储的类型必须固定(比如第一次存 map,后续不能存 int)
  • 存储引用类型(如 map、slice)时,必须拷贝后修改,避免并发读写原对象
go 复制代码
package main

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

func main() {

	var date atomic.Value
	// 1. 存储数据
	date.Store(map[string]time.Time{"time1": time.Now()})

	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			// 2. 加载数据
			oldMap := date.Load().(map[string]time.Time)
			// 3. 拷贝旧数据(避免修改原数据)
			newMap := make(map[string]time.Time)
			for k, v := range oldMap {
				newMap[k] = v
			}
			buf := bytes.NewBuffer([]byte{})
			buf.WriteString("time")
			buf.WriteString(strconv.Itoa(i))
			newMap[buf.String()] = time.Now()
			date.Store(newMap)
		}()
	}
	wg.Wait()
	fmt.Println(len(date.Load().(map[string]time.Time)))
	fmt.Println(date.Load().(map[string]time.Time))
	fmt.Println("main done")
}

五、与mutex比较

特性 atomic sync.Mutex
适用场景 单个变量的原子操作 (如计数、状态标记 复杂逻辑 / 多个变量的原子操作(比如转账(扣减 A 账户 + 增加 B 账户))
性能 极高(硬件指令级) 较低(有上下文切换开销)
使用复杂度 简单(仅支持基本操作) 灵活(支持任意逻辑)
竞态保护 仅保护变量读写 保护代码块
相关推荐
2401_874732532 小时前
C++中的装饰器模式高级应用
开发语言·c++·算法
lly2024062 小时前
《Foundation 分页》
开发语言
人道领域2 小时前
Day | 09 【苍穹外卖:订单售后业务】
java·数据库·后端
m0_662577972 小时前
模板编译期哈希计算
开发语言·c++·算法
m0_662577972 小时前
C++代码静态检测
开发语言·c++·算法
阿贵---2 小时前
编译器命令选项优化
开发语言·c++·算法
add45a2 小时前
分布式计算C++库
开发语言·c++·算法
「QT(C++)开发工程师」2 小时前
C++并发编程新纪元:线程库、异步操作与泛型Lambda深度解析
开发语言·c++
-许平安-2 小时前
MCP项目笔记四(Transport)
开发语言·c++·笔记·ai·mcp