C++ 中 std::atomic 详解:从原子操作到无锁编程
C 中 std::atomic 详解从原子操作到无锁编程一、引言多线程世界的确定性操作在多线程编程中最隐蔽、最难调试的 bug 之一就是数据竞争(Data Race)——两个线程同时访问同一内存位置至少一个是写操作且没有同步机制。数据竞争导致的行为是未定义的程序可能崩溃、产生错误结果或者更糟——表面正确但随时可能出错。std::atomic是 C11 引入的解决数据竞争的基础设施。它将变量的读写操作提升为原子操作——一个不可被中断、不可被分割的完整操作。在多线程环境中对std::atomic对象的操作总是无数据竞争的不需要额外的互斥锁保护。二、核心概念速览| 维度 | 说明 || --- | --- || 原子性(Atomicity) | 操作不可分割——要么完全执行要么完全不执行不会出现中间状态 || 数据竞争 | 两个线程同时访问同一内存至少一个写且无同步 → 未定义行为 || 内存序(Memory Order) | 控制原子操作在多大程度上约束编译器和 CPU 的指令重排 || 无锁(Lock-Free) | 大多数 atomic 类型使用 CPU 原子指令实现不依赖操作系统锁 || CAS | Compare-And-Swap(比较并交换)最重要的原子操作原语 |三、为什么需要 atomic3.1 非原子操作的经典问题cpp复制下载#include thread #include iostream int counter 0; // 非原子变量 void increment() { for (int i 0; i 100000; i) { counter; // ❌ 数据竞争 // counter 实际上分为三步 // 1. 从内存读取 counter 到寄存器 // 2. 寄存器值 1 // 3. 将寄存器值写回内存 // 两个线程可能交错执行这三步导致丢失更新 } } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout Counter: counter std::endl; // 期望 200000实际结果不确定通常小于 200000 }图表代码下载全屏3.2 使用 atomic 解决cpp复制下载#include atomic #include thread #include iostream std::atomicint counter{0}; // 原子变量 void increment() { for (int i 0; i 100000; i) { counter; // ✓ 原子操作无数据竞争 } } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout Counter: counter std::endl; // 200000 }四、atomic 的基本操作4.1 核心 APIcpp复制下载#include atomic #include iostream int main() { std::atomicint value{0}; // 基本读写 value.store(42); // 原子写入 int x value.load(); // 原子读取 // 常用操作 value 100; // 等价于 store(100) int y value; // 等价于 load() // 读-改-写操作 int old value.fetch_add(10); // 原子地old value; value 10; int prev value; // 原子地prev value; value 1; int next value; // 原子地value 1; next value; value.fetch_sub(5); // 原子地 value - 5 value 0xFF; // 原子地 value value 0xFF value | 0x80; // 原子地 value value | 0x80 // 交换操作 int oldValue value.exchange(999); // 原子地oldValue value; value 999; // CAS (Compare-And-Swap) int expected 999; bool success value.compare_exchange_strong(expected, 1000); // 如果 value expected则 value 1000返回 true // 否则 expected value返回 false std::cout Final value: value.load() std::endl; }4.2 关键操作详解| 操作 | 说明 | 是否原子 || --- | --- | --- || load() / store(val) | 读取 / 写入 | 是 || exchange(val) | 写入新值返回旧值 | 是 || fetch_add(n) / fetch_sub(n) | 增加/减少返回旧值 | 是 || operator / operator-- | 前置/后置自增减 | 是 || compare_exchange_strong(expected, desired) | 如果值等于 expected则设为 desired | 是 || compare_exchange_weak(expected, desired) | 同上但允许伪失败 | 是 || is_lock_free() | 检查是否使用 CPU 原子指令 | — |五、Compare-And-Swap (CAS)5.1 CAS 的工作原理CAS 是无锁编程的基石。它原子地执行“如果当前值等于我期望的值就更新为新值否则不更新。”cpp复制下载#include atomic #include iostream int main() { std::atomicint value{100}; int expected 100; int desired 200; // 尝试将 value 从 100 改为 200 if (value.compare_exchange_strong(expected, desired)) { std::cout CAS succeeded: value is now value std::endl; } else { std::cout CAS failed: value is value , expected updated to expected std::endl; } // 如果失败expected 会被更新为 value 的当前值 // 可以基于新的 expected 重试 while (!value.compare_exchange_weak(expected, desired)) { // expected 自动更新循环重试直到成功 desired expected * 2; // 基于新值计算 desired } }5.2 strong vs weak| 版本 | 行为 | 适用场景 || --- | --- | --- || compare_exchange_strong | 保证只在值不等时才失败 | 简单直接的 CAS 操作 || compare_exchange_weak | 即使值相等也可能伪失败 | 循环重试中性能更好 |cpp复制下载// weak 版本的正确用法在循环中使用 std::atomicint val{0}; int expected val.load(); while (!val.compare_exchange_weak(expected, expected 1)) { // expected 被自动更新重试即可 // 即使伪失败循环也会正确处理 }六、内存序(Memory Order)6.1 内存序的概念内存序控制原子操作在多线程环境下的可见性顺序。它是一把双刃剑越严格的顺序保证越安全但越慢越宽松的顺序性能越好但需要更仔细的设计。cpp复制下载#include atomic #include thread #include iostream std::atomicbool ready{false}; std::atomicint data{0}; // 线程 A生产者 void producer() { data.store(42, std::memory_order_relaxed); // 1. 先准备数据 ready.store(true, std::memory_order_release); // 2. 发布就绪信号 } // 线程 B消费者 void consumer() { while (!ready.load(std::memory_order_acquire)); // 3. 获取就绪信号 std::cout data.load(std::memory_order_relaxed) std::endl; // 4. 读取数据 // 由于 release-acquire 的同步关系 // 4 保证能看到 1 写入的 42 }6.2 六种内存序图表代码下载全屏6.3 内存序选择指南| 内存序 | 性能 | 保证 | 典型场景 || --- | --- | --- | --- || seq_cst默认 | 最慢 | 全局顺序一致性 | 不确定用什么时的安全选择 || acquire | 较快 | 后续操作不能前移 | 读锁、消费数据 || release | 较快 | 之前操作不能后移 | 写锁、发布数据 || acq_rel | 中 | 同时具有 acquire 和 release | 单个原子 RMW 操作 || relaxed | 最快 | 仅原子性 | 简单计数器、无依赖关系 |七、atomic 的实际应用7.1 无锁计数器cpp复制下载#include atomic #include thread #include vector #include iostream class ThreadSafeCounter { std::atomiclong long count_{0}; public: void increment() { count_.fetch_add(1, std::memory_order_relaxed); // 只需原子性 } long long get() const { return count_.load(std::memory_order_relaxed); } }; int main() { ThreadSafeCounter counter; std::vectorstd::thread threads; for (int i 0; i 10; i) { threads.emplace_back([counter]() { for (int j 0; j 100000; j) { counter.increment(); } }); } for (auto t : threads) t.join(); std::cout Count: counter.get() std::endl; // 1,000,000 }7.2 自旋锁(Spinlock)cpp复制下载#include atomic #include thread class Spinlock { std::atomic_flag flag_ ATOMIC_FLAG_INIT; // 最简单的原子类型 public: void lock() { // 自旋直到成功设置标志 while (flag_.test_and_set(std::memory_order_acquire)) { // CPU 提示正在自旋可选降低功耗 #if defined(__cpp_lib_atomic_wait) flag_.wait(true, std::memory_order_relaxed); #endif } } void unlock() { flag_.clear(std::memory_order_release); #if defined(__cpp_lib_atomic_wait) flag_.notify_one(); #endif } }; // 使用自旋锁保护临界区 Spinlock spin; int sharedData 0; void safeIncrement() { spin.lock(); sharedData; // 临界区 spin.unlock(); }7.3 无锁链表节点cpp复制下载#include atomic #include memory #include iostream templatetypename T class LockFreeStack { struct Node { T data; Node* next; Node(const T val) : data(val), next(nullptr) { } }; std::atomicNode* head_{nullptr}; public: void push(const T value) { Node* newNode new Node(value); newNode-next head_.load(std::memory_order_relaxed); // CAS 循环尝试将 head 更新为 newNode while (!head_.compare_exchange_weak( newNode-next, newNode, std::memory_order_release, std::memory_order_relaxed)) { // CAS 失败时newNode-next 已被更新为当前 head // 重试即可 } } bool pop(T result) { Node* oldHead head_.load(std::memory_order_relaxed); while (oldHead ! nullptr) { if (head_.compare_exchange_weak( oldHead, oldHead-next, std::memory_order_acquire, std::memory_order_relaxed)) { result oldHead-data; delete oldHead; // 注意此时其他线程可能仍持有旧指针 return true; } } return false; } ~LockFreeStack() { T dummy; while (pop(dummy)) { } } };7.4 单例模式cpp复制下载#include atomic #include memory #include mutex class Singleton { public: static Singleton getInstance() { // 第一次检查无锁快速路径 Singleton* instance instance_.load(std::memory_order_acquire); if (!instance) { std::lock_guardstd::mutex lock(mutex_); // 第二次检查持锁防止重复创建 instance instance_.load(std::memory_order_relaxed); if (!instance) { instance new Singleton(); instance_.store(instance, std::memory_order_release); } } return *instance; } private: Singleton() default; static std::atomicSingleton* instance_; static std::mutex mutex_; }; std::atomicSingleton* Singleton::instance_{nullptr}; std::mutex Singleton::mutex_;八、atomic vs mutex 对比| 维度 | std::atomic | std::mutex || --- | --- | --- || 操作粒度 | 单个变量 | 代码块 || 阻塞行为 | 无阻塞 | 可能阻塞 || 开销 | 极低CPU 指令级别 | 较高可能系统调用 || 适用场景 | 简单计数器、标志位 | 复杂临界区 || 编程难度 | 高需理解内存序 | 低 || 死锁风险 | 无 | 有 |九、选择决策流程图表代码下载全屏十、总结std::atomic是 C 中实现线程安全操作的基础工具理解它需要把握以下要点原子性的本质原子操作是不可分割的——在多线程环境中对atomic变量的操作要么完全执行要么完全不执行不存在中间状态被其他线程观察到的情况。这是消除数据竞争的根本保证。核心操作load/store基本读写fetch_add/fetch_sub原子算术操作exchange交换值compare_exchange_strong/weak(CAS)比较并交换无锁编程的基石内存序的权衡seq_cst默认最安全性能最低。不确定时使用acquire/release成对使用实现同步性能较好relaxed仅保证原子性性能最高。适用于简单计数器降低内存序要求可以显著提升性能但需要仔细分析线程间的依赖关系适用场景首选场景简单的计数器、布尔标志、指针赋值谨慎使用CAS 循环实现无锁数据结构需要深入理解内存序和 ABA 问题不适合的场景需要同时修改多个变量、复杂的业务逻辑应使用mutex最佳实践默认使用seq_cst确认安全后逐步降低内存序要求CAS 循环使用compare_exchange_weak以获得更好的性能使用is_lock_free()检查原子操作的实现方式避免在atomic和mutex之间过度优化——简单正确的代码往往比微妙的优化更重要std::atomic是 C 并发编程的基础模块它既是构建高性能无锁数据结构的基石也是日常多线程编程中替代mutex的轻量级选择。掌握它就能在多线程世界中写出正确且高效的代码。

相关新闻

90% 新手踩坑汇总:Hermes 整合包下载解压运行全套避坑实操

90% 新手踩坑汇总:Hermes 整合包下载解压运行全套避坑实操

🔍前言 许多尝试在本地部署 AI 智能体的用户,常常被 Hermes 原生部署的复杂配置流程所困扰。传统的源码搭建方式需要手动匹配特定版本的 Python 和 Node.js,批量安装大量第三方依赖,并逐一调试系统环境变量、解决端口占用、修复路…

2026/10/18 0:34:24 阅读更多 →
C++ 中 weak ptr 详解:打破循环引用的观察者指针

C++ 中 weak ptr 详解:打破循环引用的观察者指针

C 中 weak_ptr 详解:打破循环引用的观察者指针一、引言:不参与所有权的智能指针std::weak_ptr 是 C11 引入的智能指针,它是 std::shared_ptr 的配套工具。与 shared_ptr 不同,weak_ptr 不参与对象的共享所有权——它持有一个对象的…

2026/10/20 4:48:49 阅读更多 →
LTX-2.3 V1.6:基于int8量化的低显存AI视频生成工具实践

LTX-2.3 V1.6:基于int8量化的低显存AI视频生成工具实践

如果你正在寻找一个能够将文字或图片快速转换成带音频视频的AI工具,而且希望它既不需要复杂的环境搭建,又能在普通消费级显卡上流畅运行,那么LTX-2.3工具V1.6版本值得你重点关注。传统视频生成方案往往需要高端显卡和繁琐的配置流程,而这个工具通过int8量化技术实现了2-4倍…

2026/10/20 8:35:44 阅读更多 →

最新新闻

Python+MySQL学生管理系统:可视化界面落地与避坑指南

Python+MySQL学生管理系统:可视化界面落地与避坑指南

简介:这是一套面向Python与数据库初学者的学生管理系统实战项目,采用Python编写客户端逻辑、MySQL负责数据存储,适合正在学习桌面应用开发或需要完成课程设计的学生参考。压缩包共2个文件,包含1个py主程序与1个sql数据库脚本&…

2026/10/20 17:19:49 阅读更多 →
MySQL反斜杠转义全解析:从字符串插入到LIKE查询的避坑指南

MySQL反斜杠转义全解析:从字符串插入到LIKE查询的避坑指南

先交代一个真实翻车现场。有天业务方发来一张截图,某张配置表里的 Windows 路径字段,原本应该是C:\Users\某应用目录\data\config.ini,结果库里存成了C:Users某应用目录dataconfig.ini,所有反斜杠集体失踪。我一开始以为是应用层写…

2026/10/20 17:19:49 阅读更多 →
电商评论情感分析实战:从数据清洗到BERT蒸馏部署全记录

电商评论情感分析实战:从数据清洗到BERT蒸馏部署全记录

这个项目标题一看就是从实战里踩出来的,数据坑、部署、硬核笔记,说明作者真刀真枪跑过完整链路,不是拿公开数据集跑个demo就完事。文本分类和情感分析在NLP里属于入门必练但做好极难的方向,难就难在数据永远是脏的、标签永远是偏的…

2026/10/20 17:19:49 阅读更多 →
TensorFlow工业级AI工程实践:从计算图到TFX流水线

TensorFlow工业级AI工程实践:从计算图到TFX流水线

1. 这不是“学个框架”那么简单:TensorFlow到底在解决什么问题?很多人第一次听说TensorFlow,是在某次技术分享会上听到“谷歌开源的深度学习框架”,或者在招聘JD里看到“熟悉TensorFlow者优先”。但如果你真去翻它的GitHub仓库、读…

2026/10/20 17:19:49 阅读更多 →
双线性插值原理与C语言实现:图像缩放算法详解

双线性插值原理与C语言实现:图像缩放算法详解

简介:这份资源围绕双线性插值方法及实现展开,面向图像处理、计算机图形学与数值计算方向的学习者,尤其适合希望用C语言理解插值数学原理并动手实现的开发者。包内共31个文件,以h头文件、cpp源文件、obj目标文件为主,辅…

2026/10/20 17:19:49 阅读更多 →
Prescan+Simulink+CarSim联仿:多工况自动驾驶避障模型搭建与标定实战

Prescan+Simulink+CarSim联仿:多工况自动驾驶避障模型搭建与标定实战

做自动驾驶避障仿真的人,迟早会跟三个名字绑在一起:Prescan、Simulink、CarSim。标题里那套“不同工况避障模型”,说白了就是让一辆装有虚拟传感器的车,在模拟道路环境里遇到静止车、前车急减速、旁边车道突然切进来、行人横穿这些…

2026/10/20 17:18:48 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →