当前位置: 首页 > 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.jsqmd.com/news/960321/

相关文章:

  • PHP安全漏洞检测与修复技术解析
  • 从原理到像素:我是如何用C++和Qt从头实现一个可交互的CIE1931色度图绘制引擎的
  • [智能体-292]:人类自然语言精髓:符号为壳,语境为坐标系|语言演化 + 人脑高情商语义理解全解
  • 告别信号模糊:手把手教你配置AD9361的RSSI,实现精准功率测量
  • 【毕业设计】基于springboot后端微信小程序的丽江市旅游分享平台基于springboot+微信小程序的丽江市旅游分享平台(源码+文档+远程调试,全bao定制等)
  • 从‘数字底片’到成片:新手必学的Photoshop Camera RAW基础设置(色彩空间、JPG支持)
  • 2026年q2:抗粘黏dlc涂层/活塞杆dlc涂层/疏水dlc涂层/真空镀膜dlc涂层/类金刚石dlc涂层/ta - 优质品牌商家
  • 避坑指南:Termux安装Linux桌面时,关于音频、网络和性能的那些事儿
  • 电感与磁珠的本质区别:从储能与耗能原理到工程选型实战
  • 基于Python与Web架构的EEG研究IDE:从实验设计到数据分析的全流程自动化
  • 注塑机怎么选?从类型、锁模力到产区厂商,选型全指南
  • 2023数据科学实战生存指南:从业务定义到可信数据落地
  • 2026年东莞商家小程序怎么做
  • 多维聚合后的数据操作:从GROUP BY到立方体拓扑思维
  • G-Helper:华硕笔记本用户的终极轻量级控制指南
  • RapidIn:面向大模型的逐词级训练数据影响力溯源技术
  • 硬件工程师面试实战指南:从简历优化到技术深挖的22家公司经验复盘
  • 2026年腾讯云OpenClaw/Hermes Agent配置Token Plan超详细安装教程
  • Mythos能力解析:大模型多步推理与跨文档验证的质变突破
  • Bilibili视频转文字终极指南:如何一键将B站视频转为可编辑文字稿?
  • 众智商学院官方网址及电话信息公示FAQ - 众智商学院课程中心
  • Hutool FileUtil实战:从日志清理到文件同步,3个真实项目场景应用
  • 淘宝买的CARSIM2020安装包,实测保姆级安装与破解教程(含HostID替换避坑指南)
  • PyTorch为何成为TVA的“大脑皮层“(9)
  • 如何将视频从 iPhone 发送到 OnePlus?
  • 从原理到调参:深入Matlab Hilbert变换,教你画出更精准的包络线
  • 从VGG16到ResNet18:何恺明当年到底解决了什么‘训练难题’?用Keras对比实验告诉你
  • Python装饰器实战:从闭包原理到高精度日志与智能重试
  • 2026年Q2手套箱植绒加工技术选型与供应商解析 - 优质品牌商家
  • GCP生产级MLflow安全部署:Cloud Run+IAP+VPC egress实战指南