当前位置: 首页 > news >正文

atomic 原子操作真的“原子“吗?CPU 指令真相解析

atomic 原子操作真的"原子"吗?CPU 指令真相解析

前言

atomic 包是 Go 并发编程的利器,速度快、无锁、不阻塞。但很多人不知道,atomic 操作在底层是怎么实现的。

atomic 操作真的"原子"吗?本文研究了 CPU 指令后发现,有些操作是真正的原子,有些只是"看起来原子"。今天来聊聊。

一、 底层原理

1.1 atomic 的底层实现

atomic 操作依赖 CPU 的原子指令:

graph TD A["atomic.AddInt64"] --> B["CPU 指令"] B --> C["LOCK 前缀"] C --> D["内存总线锁定"] D --> E["原子操作"] E --> F["返回新值"] G["CAS"] --> H["比较并交换"] H --> I["原子指令"] I --> J["成功或失败"]

关键点:

  • LOCK 前缀确保指令原子性
  • 内存总线锁定影响其他 CPU
  • CAS 是无锁编程的基础
  • 不同 CPU 架构实现不同

1.2 atomic 操作分类

操作指令原子性性能
AddInt64LOCK XADD真原子
LoadInt64LOCK MOV真原子
StoreInt64LOCK MOV真原子
CompareAndSwapLOCK CMPXCHG真原子
SwapLOCK XCHG真原子

二、 快速上手

2.1 atomic 的基本使用

package main import ( "fmt" "sync" "sync/atomic" ) var count int64 func main() { var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 100000; j++ { atomic.AddInt64(&count, 1) } }() } wg.Wait() fmt.Printf("count: %d\n", count) }

2.2 atomic 与 Mutex 对比

import ( "sync" "sync/atomic" "time" ) var ( atomicCount int64 mutexCount int64 mu sync.Mutex ) func atomicInc() { atomic.AddInt64(&atomicCount, 1) } func mutexInc() { mu.Lock() mutexCount++ mu.Unlock() } func main() { n := 10000000 start := time.Now() for i := 0; i < n; i++ { atomicInc() } fmt.Printf("atomic: %v\n", time.Since(start)) start = time.Now() for i := 0; i < n; i++ { mutexInc() } fmt.Printf("mutex: %v\n", time.Since(start)) }

三、 核心 API / 深水区

3.1 atomic 操作速查

函数功能注意事项
AddInt64原子加对齐要求
LoadInt64原子读防止乱序
StoreInt64原子写防止乱序
CompareAndSwapCAS乐观锁
Swap交换返回旧值

3.2 内存对齐要求

// 错误:没有对齐 type BadCounter struct { flag byte count int64 // 可能没对齐 } // 正确:保证对齐 type GoodCounter struct { count int64 // 8 字节对齐 flag byte }

3.3 CAS 乐观锁实现

var value int64 func update(newValue int64) bool { for { old := atomic.LoadInt64(&value) if atomic.CompareAndSwapInt64(&value, old, newValue) { return true } // 失败,重试 } }

四、 实战演练

4.1 高性能计数器

package main import ( "fmt" "sync" "sync/atomic" "time" ) type AtomicCounter struct { count int64 } func (c *AtomicCounter) Inc() { atomic.AddInt64(&c.count, 1) } func (c *AtomicCounter) Dec() { atomic.AddInt64(&c.count, -1) } func (c *AtomicCounter) Value() int64 { return atomic.LoadInt64(&c.count) } func main() { counter := &AtomicCounter{} var wg sync.WaitGroup start := time.Now() for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 100000; j++ { counter.Inc() } }() } wg.Wait() fmt.Printf("耗时: %v, count: %d\n", time.Since(start), counter.Value()) }

五、 避坑指南与最佳实践

💡技巧:读操作也用 Load
不加 Load 可能读到旧值,因为 CPU 乱序执行。

⚠️警告:atomic 不能替代 Mutex
复杂临界区,还得上 Mutex。

推荐:简单计数器用 atomic
高频场景,atomic 是你的朋友。

六、 综合实战演示

6.1 生产级并发限流器

package main import ( "fmt" "sync" "sync/atomic" "time" ) type RateLimiter struct { limit int64 current int64 interval time.Duration lastTime time.Time } func NewRateLimiter(limit int64, interval time.Duration) *RateLimiter { return &RateLimiter{ limit: limit, interval: interval, lastTime: time.Now(), } } func (rl *RateLimiter) Allow() bool { now := time.Now() if now.Sub(rl.lastTime) >= rl.interval { atomic.StoreInt64(&rl.current, 0) rl.lastTime = now } current := atomic.AddInt64(&rl.current, 1) return current <= rl.limit } func main() { rl := NewRateLimiter(100, time.Second) var wg sync.WaitGroup passed := int64(0) blocked := int64(0) for i := 0; i < 200; i++ { wg.Add(1) go func() { defer wg.Done() if rl.Allow() { atomic.AddInt64(&passed, 1) } else { atomic.AddInt64(&blocked, 1) } }() } wg.Wait() fmt.Printf("通过: %d, 阻塞: %d\n", passed, blocked) }

总结

atomic 的核心要点:

  • 依赖 CPU 原子指令
  • 内存对齐很重要
  • CAS 是无锁编程基础
  • 读操作也要用 Load
http://www.gsyq.cn/news/1472001.html

相关文章:

  • 2026年达州全屋定制工厂实力排行:达州星平方全屋定制工厂口碑怎么样/本地品牌对比 - 优质品牌商家
  • [智能体-292]:人类自然语言精髓:符号为壳,语境为坐标系|语言演化 + 人脑高情商语义理解全解
  • 【毕业设计】基于springboot后端微信小程序的丽江市旅游分享平台基于springboot+微信小程序的丽江市旅游分享平台(源码+文档+远程调试,全bao定制等)
  • 避坑指南:Termux安装Linux桌面时,关于音频、网络和性能的那些事儿
  • G-Helper:华硕笔记本用户的终极轻量级控制指南
  • 2026年东莞商家小程序怎么做
  • Hutool FileUtil实战:从日志清理到文件同步,3个真实项目场景应用
  • 淘宝买的CARSIM2020安装包,实测保姆级安装与破解教程(含HostID替换避坑指南)
  • 2026年C语言就业情况如何?想进IT大厂有机会吗?
  • 解决ISE调用ModelSim仿真失败:vlib work库创建问题深度解析
  • 淘宝买的CARSIM2020安装包,从下载到破解的保姆级避坑指南(含HostID获取)
  • 保姆级教程:给你的PyTorch模型装上‘X光’——TensorBoard逐层可视化权重与激活实战
  • 2025-2026年北京润府电话查询:看房前需了解项目定位与注意事项 - 品牌推荐
  • MCP协议实战:AI工程师的模型可控性架构指南
  • 告别枯燥时序图:用‘父子对话’和‘聊天应答’比喻彻底搞懂IIC协议(附STM32驱动OLED实例)
  • USMART:嵌入式实时交互调试组件原理、移植与实战
  • 智慧树网课自动化助手:解放双手的终极学习解决方案
  • 终极指南:5个关键步骤让你的NVIDIA显卡性能飙升
  • Codeforces胡萝卜插件:从数据焦虑到精准预测的浏览器扩展革命
  • MicroBlaze LWIP项目资源优化实录:中断精简与LUT节省如何为SPI Bootloader腾出空间
  • 深入Linux V4L2异步匹配:从设备树(DTS)配置到驱动probe的完整链路解析
  • Django+Vue双端图书借阅系统源码包(含MySQL数据库脚本与一键部署指南)
  • Ansible管理Windows主机避坑实录:从‘No module named winrm’到成功执行win_ping的全流程排错指南
  • S32K144裸机环境下基于SysTick的可配置微秒延时驱动(1μs~1000μs)
  • KAG vs RAG:结构化知识注入如何提升AI推理可控性
  • 从傅里叶到拉普拉斯:搞懂‘复频域’到底在分析什么(给控制/通信新人的避坑指南)
  • 硬件工程师必备:稳压二极管代换手册与实战选型指南
  • 从GPT-2到GDPR:NLP工程师必须知道的5个伦理实战避坑指南
  • 上下文工程:让RAG系统真正可信的实战方法论
  • 智慧树刷课插件:5分钟实现自动化学习的终极解决方案