原子操作与原子指令:一个承诺,多种实现
一句话原子操作是一种效果上的承诺在别人看来这件事要么完全没发生要么已经全部完成看不到中间状态。原子指令是 CPU 提供的具体工具一条在硬件层面保证不可分割的机器指令。原子操作经常靠原子指令实现但两者并不一一对应一个原子操作可能需要好几条指令也可能根本不需要特殊指令反过来“一条指令也不等于原子”。一、先打个比方银行转账你在手机上给朋友转账 100 元。层次发生了什么原子性体现在哪你看到的“转账成功或转账失败”不会出现我扣了钱朋友没收到银行系统数据库事务扣款 入账事务要么全部提交要么全部回滚数据库内部加锁、写日志锁保护多个步骤不被插队锁的内部一条 CPU 指令抢占锁标志这里才用到原子指令转账是一个原子操作但它显然不是一条指令完成的。它是在许多层的协作下对外表现为不可分割。银行并发编程转账要么全成、要么全不成原子操作一种对外可见的效果、一种语义金库门上那把只能一次转动到位的机械锁原子指令硬件提供的最底层工具中间的事务、锁、日志用工具搭出承诺的各种手段原子操作说的是看起来怎样原子指令说的是硬件怎么做。二、各自的定义原子操作Atomic Operation一个抽象概念定义一个操作在其他观察者看来是不可分割的不存在做了一半的可见状态。它可以出现在任何层次层次原子操作的例子硬件对一个对齐的 64 位变量原子加 1编程语言C# 的Interlocked.Increment、C 的std::atomicint::fetch_add操作系统文件系统的rename替换文件要么是旧的、要么是新的数据库事务的 ACID 中的A游戏业务交易时物品和金币同时交换原子指令Atomic Instruction一个具体的硬件设施定义CPU 指令集中能保证读-改-写在多核环境下不被其他核心插入的指令。平台原子指令说明x86 / x64LOCK ADD、LOCK XADD、LOCK CMPXCHG、LOCK OR等LOCK前缀让读-改-写变为原子x86 / x64XCHG与内存交换隐含LOCK不写也是原子的x86 / x64CMPXCHG16B一次比较交换 16 字节ARMv8.0LDXR/STXRLL/SC两条指令配合单独一条都不算完整的原子操作ARMv8.1LSECAS、LDADD、SWP、LDSET等单条原子指令并带有 acquire / release 变体三、核心关系不是一一对应┌─────────── 原子操作语义看起来不可分割───────────┐ │ │ 一条原子指令 多条指令组合 CAS 循环 锁 普通对齐读写 (LOCK XADD) (LDXR/STXR) (原子取最大值) (事务、交换背包) (MOV) │ │ └──────────── 最底层几乎都依赖缓存一致性 原子指令 ─────┘分几种情况看。情况一一个原子操作 一条原子指令最理想的情况。C# 的原子加 1在 x64 上就是一条指令Interlocked.Increment(refcounter);; x64 mov eax, 1 lock xadd dword ptr [rcx], eax ; 原子地取出旧值同时内存值 1 inc eax ; 返回新值情况二一个原子操作 多条指令LL/SC同一行 C# 代码在较老的 ARM 手机ARMv8.0上变成一个小循环; ARMv8.0 retry: ldaxr w1, [x0] ; 加载同时在这块内存上做个标记独占监视器 add w1, w1, #1 stlxr w2, w1, [x0] ; 如果标记还在才存储w2 0 成功1 失败 cbnz w2, retry ; 失败就重来四条指令里没有哪一条单独是原子加但组合起来对外表现为原子加。这正是原子操作和原子指令不同的最好证据。在 ARMv8.1 及以上又变回一条; ARMv8.1 LSE mov w1, #1 ldaddal w1, w2, [x0] ; 原子加带 acquire release 语义同一个原子操作在不同 CPU 上可能是 1 条指令也可能是 4 条指令的循环。语义不变实现不同。情况三用原子指令拼出更大的原子操作硬件没有原子取最大值这条指令x86 没有ARM LSE 其实有LDSMAXC# 就用 CAS 循环拼出来publicstaticvoidUpdateMax(refintmax,intvalue){intcurrentVolatile.Read(refmax);while(valuecurrent){intseenInterlocked.CompareExchange(refmax,value,current);if(seencurrent)return;currentseen;}}一个原子操作用了多次原子指令外加普通指令。所有无锁数据结构都是这么搭起来的。情况四用锁实现任意大小的原子操作交换两个玩家背包里的物品涉及好几个字段任何一条硬件指令都覆盖不了lock(tradeLock){playerA.Items.Remove(sword);playerB.Items.Add(sword);playerB.Gold-500;playerA.Gold500;}// 其他线程看不到剑已经给出去、钱还没到的中间状态而lock本身在最底层也是靠一条原子指令CAS 或交换去抢锁标志。C 里甚至会自动这么做std::atomicBigStruct如果结构体太大硬件没有对应的原子指令标准库就会悄悄用内部锁实现。可以用is_lock_free()或is_always_lock_free检查。情况五原子操作但不需要特殊指令对齐的、不超过机器字长的普通读写本身就是原子的不会撕裂inthpplayer.Health;// 读一个对齐的 int普通 MOV 指令天然原子mov eax, dword ptr [rcx] ; 没有 LOCK也不是原子指令但读出来的值是完整的不会出现读到了一半新值、一半旧值。但注意它只保证单次读或单次写原子读 → 改 → 写组合在一起就不原子了见下一节。情况六单核时代的另一种做法在单核单片机上没有其他核心来插队唯一的打断来源是中断。所以关中断 → 做事 → 开中断就能构成原子操作完全不需要原子指令。这也说明原子性是相对于观察者而言的观察者变了实现手段也会变。汇总原子操作的实现方式用到原子指令吗例子单条原子指令1 条x64 上的Interlocked.Increment指令组合LL/SC一对配合的指令ARMv8.0 上的Interlocked.IncrementCAS 循环多次原子取最大值、无锁栈锁间接锁内部用交换背包物品普通对齐读写不需要读一个int关中断不需要单核单片机四、反过来看一条指令 ≠ 原子这是最常见的误解“counter编译成一条指令所以它是原子的”。counter;在 x64 上它可能真的被编译成一条指令add dword ptr [rcx], 1 ; 一条指令但没有 LOCK 前缀可在 CPU 内部这条指令会被拆成三步微操作读取 [rcx] → 加 1 → 写回 [rcx]单核时没问题多核时就会被插队核心 1读取 10 ──────── 加 1 → 写回 11 核心 2 读取 10 ── 加 1 → 写回 11 结果加了两次只变成了 11加上LOCK前缀CPU 才会在整个读-改-写期间把这条缓存行抓在手里不允许别的核心插进来lock add dword ptr [rcx], 1 ; 这才是原子指令写法指令数量多核下是否原子counter→add [mem], 11 条❌Interlocked.Increment→lock xadd1 条✅ARMv8.0 的 LL/SC 循环4 条✅原子与否看的是能不能被插队而不是指令有几条。再一个陷阱撕裂Tearing情况是否原子64 位系统上读写对齐的long✅32 位系统上读写long❌ 可能拆成两次 32 位操作读到一半新一半旧未对齐的数据、跨越两条缓存行的数据❌ 不保证读写struct比如Vector312 字节❌ 三个字段分开写可能读到混合值C# 规范只保证不超过指针大小、正确对齐的读写是原子的。在 32 位平台读long要用Interlocked.Read。五、硬件是怎么让指令变原子的原子指令也不是魔法它依赖缓存一致性协议伪共享那一篇讲过的 MESI机制原理代价缓存锁定把目标缓存行拿到本核并设为独占状态执行期间拒绝其他核心的请求正常情况几纳秒到几十纳秒总线锁定数据跨越两条缓存行Split Lock时只能锁住整个内存总线极慢而且会拖慢所有核心新的 Intel CPU 和 Linux 可以检测并报警独占监视器LL/SCLDXR时登记这块地址期间如果被别人写了STXR就失败不阻塞别人但可能失败重试缓存锁定 核心 1 [拿到缓存行独占权] 读 → 改 → 写 [放开] 核心 2 想访问同一缓存行请稍等 …… LL/SC 核心 1 LDXR登记…… STXR检查登记还在吗 核心 2 写了这块内存 → 核心 1 的登记被清除 → STXR 失败 → 重来这也解释了为什么原子变量一定要对齐对齐的数据只占一条缓存行可以用便宜的缓存锁定跨行的数据会退化成昂贵的总线锁。六、原子性 ≠ 顺序性另一个容易混淆的维度原子操作保证的是这一个变量的修改不可分割并不自动保证它和其他变量之间的先后顺序。这是上一篇内存顺序讲的内容。C 把这两个维度分得很清楚std::atomicintx;x.fetch_add(1,std::memory_order_relaxed);// 原子但不约束前后其他读写的顺序x.fetch_add(1,std::memory_order_seq_cst);// 原子且是完整的内存屏障同一个原子操作选择不同的内存顺序可能对应不同的原子指令操作x64ARMv8.1原子加relaxedlock xaddldadd原子加acquirelock xaddldadda原子加releaselock xaddldaddl原子加seq_cstlock xaddldaddal原子读acquire普通movldar原子写seq_cstxchg或movmfencestlr两个有意思的观察观察原因x64 上不管什么顺序都是lock xaddx86 的LOCK指令自带完整屏障想弱也弱不了x64 上 acquire 读只是普通movx86 内存模型较强普通读本身就有 acquire 语义所以“原子回答的是会不会被插队”“内存顺序回答的是别人以什么顺序看到”。两者是正交的两个问题。C# 的Interlocked把两者打包成原子 完整屏障简单但没得选Volatile.Read/Write则只提供顺序保证不提供读-改-写的原子性。七、游戏开发中的一个巧妙用法用一条原子指令发布一大块数据既然硬件只能原子地修改一个机器字那怎么让一个很大的数据整体原子地更新答案不修改数据本身只原子地替换指向它的指针。usingSystem.Threading;// 不可变的配置快照创建后不再修改publicsealedclassGameConfig{publicreadonlyfloatDropRate;publicreadonlyintMaxPlayers;publicreadonlystringMapName;publicGameConfig(floatdropRate,intmaxPlayers,stringmapName){DropRatedropRate;MaxPlayersmaxPlayers;MapNamemapName;}}publicstaticclassConfigCenter{staticGameConfigcurrentnewGameConfig(0.1f,100,Island);// 读者读到的永远是一个完整的版本不会是新掉率 旧地图publicstaticGameConfigCurrentVolatile.Read(refcurrent);// 热更新后台线程构建好全新的对象一次性替换引用publicstaticvoidReload(GameConfignewConfig){Volatile.Write(refcurrent,newConfig);// release对象内容先于引用可见}}旧配置对象 { 0.1, 100, Island } ← 正在用它的线程继续用不受影响 ↑ current ────────┘ ── 一次原子写 ──→ 新配置对象 { 0.2, 120, Desert }层次内容业务上的原子操作“整套配置同时生效”实际依赖的原子性一次对齐的引用写入内存顺序release 写 / acquire 读保证读到引用就能读到完整内容旧对象的回收交给 GC上一篇说的内存回收难题被 GC 免费解决这就是 RCU 和写时复制的思路把一个大的原子操作归约成一个小的原子指令。很多无锁设计的诀窍都在这里。八、常见误解误解实际情况“原子操作就是原子指令”原子操作是语义原子指令只是实现它的手段之一“一条指令就是原子的”add [mem], 1是一条指令但多核下不原子“原子操作一定不用锁”锁本身就是实现原子操作的手段C 的大号std::atomic内部可能就是锁“原子就意味着线程安全”两个各自原子的操作连在一起整体并不原子先检查再修改就是经典反例“原子操作保证了顺序”原子性和内存顺序是两回事relaxed原子操作不约束顺序“volatile能让i原子”不能。C# 的volatile只提供顺序语义C 的volatile连顺序都不保证“原子指令很便宜”无竞争时便宜高竞争时缓存行在核心间乒乓跨缓存行时会锁总线“ARM 和 x86 的行为一样”指令完全不同内存模型强弱也不同PC 上没问题的代码可能在手机上出错两个原子操作 ≠ 一个原子操作的例子// ❌ 每一步都原子组合起来不原子if(Volatile.Read(refstock)0)// 原子读还有库存Interlocked.Decrement(refstock);// 原子减扣库存// 两个线程同时看到 stock 1都去扣结果变成 -1超卖// ✅ 把检查 修改合成一个原子操作CAS 循环ints;do{sVolatile.Read(refstock);if(s0)returnfalse;}while(Interlocked.CompareExchange(refstock,s-1,s)!s);returntrue;九、一张图总结层次关系┌─────────────────────────────────────────────────────┐ │ 业务层原子操作 交易、转账、配置热更新、背包交换 │ ├─────────────────────────────────────────────────────┤ │ 同步手段 锁、事务、CAS 循环、写时复制 │ ├─────────────────────────────────────────────────────┤ │ 语言层原子操作 Interlocked.*、std::atomic、 │ │ Volatile.Read/Write │ ├─────────────────────────────────────────────────────┤ │ 编译器 / JIT / Burst 根据目标 CPU 选择具体指令 │ ├─────────────────────────────────────────────────────┤ │ 原子指令 LOCK XADD、LOCK CMPXCHG、XCHG、 │ │ LDXR/STXR、CAS、LDADD │ ├─────────────────────────────────────────────────────┤ │ 硬件机制 缓存一致性MESI、缓存锁、总线锁、 │ │ 独占监视器 │ └─────────────────────────────────────────────────────┘ ↑ 越往上越抽象、范围越大 ↓ 越往下越具体、范围越小一句话总结原子操作是目的原子指令是手段之一。原子操作原子指令本质语义、效果、承诺具体的机器指令所在层次任何层次从硬件到业务只在 CPU 指令集范围可大可小可以涉及多个变量通常只针对一个对齐的内存位置最多 16 字节实现方式原子指令、LL/SC 组合、CAS 循环、锁、关中断……由硬件和缓存一致性协议保证跨平台语义一致x86、ARM 各不相同口诀“原子看效果不看条数指令是砖操作是房原子管插队屏障管顺序。”

相关新闻

基于5777张图片的垃圾检测数据集实战:六大场景与模型训练指南

基于5777张图片的垃圾检测数据集实战:六大场景与模型训练指南

垃圾检测在计算机视觉里是个看着简单、做起来相当磨人的细分方向。说它简单,是因为目标就是“找出画面里的垃圾”;说它磨人,是因为垃圾这东西没有固定形状、没有统一颜色、尺度跨度极大——一个矿泉水瓶和一个建筑垃圾堆,在特征层…

2026/10/18 2:39:02 阅读更多 →
Unity 性能优化:减少透明物体带来的开销

Unity 性能优化:减少透明物体带来的开销

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 2:39:02 阅读更多 →
Unity Timeline检视面板完全指南:轨道、剪辑与混合技巧详解

Unity Timeline检视面板完全指南:轨道、剪辑与混合技巧详解

做Timeline内容这几年,如果让我选一个“最容易被新手忽略、却又最值得花时间研究”的地方,我一定选检视面板。前面几篇讲了Timeline怎么建、轨道怎么摆、素材怎么拖,很多同学以为会这些就够用了,可一旦开始调细节,比如…

2026/10/18 2:39:02 阅读更多 →

最新新闻

自然语言驱动图表生成深度实战:图表狐在气候、能源等5大场景下的文本指令控制范式

自然语言驱动图表生成深度实战:图表狐在气候、能源等5大场景下的文本指令控制范式

传统图表工具需要手动操作或编写代码,而图表狐(https://graphai.crazystone.work)基于AIGC技术,让用户通过自然语言即可生成折线图、桑基图、雷达图等28种专业图表。本文通过5个跨领域实战案例(气候、运动、考古、能源…

2026/10/18 3:29:17 阅读更多 →
AIME竞赛是什么?3小时15道整数难题背后的数学背景与aime_2026数据集渊源

AIME竞赛是什么?3小时15道整数难题背后的数学背景与aime_2026数据集渊源

【免费下载链接】aime_2026 项目地址: https://ai.gitcode.com/hf_mirrors/MathArena/aime_2026 点击查看 免费下载 AIME竞赛(American Invitational Mathematics Examination,美国数学邀请赛)是全球最著名的中学生数学竞赛之一&…

2026/10/18 3:29:17 阅读更多 →
openEuler 24.03 LTS SP3安装实战:从U盘启动到系统初始化避坑指南

openEuler 24.03 LTS SP3安装实战:从U盘启动到系统初始化避坑指南

装系统这件事,在运维日常里看起来是最基础的操作,真上手了才明白坑有多深。前几天给一台业务服务器换装 openEuler 24.03 LTS SP3,从下载镜像到重启进系统,前后折腾了整整一个下午,踩了引导识别失败、老磁盘分区表残留…

2026/10/18 3:29:17 阅读更多 →
Codex 一键安装包实操分享:把 auth.json 改到 TaoToken 后几分钟完成部署

Codex 一键安装包实操分享:把 auth.json 改到 TaoToken 后几分钟完成部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 3:29:17 阅读更多 →
从零搭建轻量文章管理系统:Node.js与SQLite实战解析

从零搭建轻量文章管理系统:Node.js与SQLite实战解析

1. 项目概述与设计初衷1.1 这个标题到底说的是什么“weixin105文章管理系统”,第一眼看上去像是个内部项目代号,但拆开来看,其实是一个非常典型的内容管理需求:一套面向个人或小团队的公众号文章管理方案。weixin105这个编号&…

2026/10/18 3:29:17 阅读更多 →
ADAMS/View高级培训光盘实战:多体动力学建模、求解与参数化

ADAMS/View高级培训光盘实战:多体动力学建模、求解与参数化

简介:面向ADAMS/View中高级用户的官方培训光盘资源,适合机械、车辆及航空航天领域需要提升多体动力学仿真能力的工程师。资源包内含477个文件,大小仅4.75MB,文件类型以cmd命令脚本、jpg/htm图文资料、txt说明文本和gif动画演示为主…

2026/10/18 3:28:16 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →