0

0

C++的std::atomic如何保证操作的原子性? (无锁编程基础)

尼克

尼克

发布时间:2026-02-13 02:31:16

|

688人浏览过

|

来源于php中文网

原创

std::atomic的原子性由cpu硬件指令实现,非编译器魔法或锁机制;其操作需配合恰当memory_order防止重排,多变量协同仍需mutex。

c++的std::atomic如何保证操作的原子性? (无锁编程基础)

std::atomic 的原子性靠硬件指令实现,不是编译器魔法

它不靠锁,也不靠操作系统调度保证原子性,而是把 loadstorefetch_add 这类操作翻译成 CPU 提供的原子指令,比如 x86 上的 lock xaddmov + mfence 组合,或 ARM 上的 ldxr/stxr 循环。编译器只负责生成正确指令序列,并插入必要的内存屏障(通过 memory_order 参数控制),不会擅自重排这些操作。

常见错误现象:std::atomic<int> x = 0;</int> 然后多个线程同时执行 x++,结果却小于预期——这是因为 x++ 展开为读-改-写三步,必须用 fetch_add(1) 才真正原子;直接赋值 x = 5 是原子的,但 x += 3 不是语法糖,它等价于 x.fetch_add(3),这点容易误判。

  • std::atomic 对象必须满足 trivially copyable,且底层类型需支持对应平台的原子指令(例如 std::atomic<:string></:string> 非法)
  • 非 lock-free 类型(可通过 x.is_lock_free() 检查)会退化为内部互斥锁,性能断崖下跌,嵌入式或实时场景务必确认返回 true
  • 未指定 memory_order 时默认用 memory_order_seq_cst,安全但可能拖慢性能;高频更新计数器可考虑 memory_order_relaxed,但要自己确保逻辑无依赖

memory_order 不是可选项,选错会导致读写乱序

它决定编译器和 CPU 能否对原子操作前后其他内存访问做重排。选错不会崩溃,但会让看似正确的多线程逻辑在优化后出错——比如生产者写完数据再置标志位,消费者看到标志位就去读数据,若标志位用 memory_order_relaxed,编译器可能把数据写入移到标志位之后,消费者就拿到脏数据。

使用场景举例:自旋锁中 flag 变量通常用 memory_order_acquire(加锁)和 memory_order_release(解锁),既避免重排又比 seq_cst 轻量;计数器累加若无依赖关系,fetch_add(1, std::memory_order_relaxed) 完全够用。

立即学习C++免费学习笔记(深入)”;

CreBee
CreBee

短视频矩阵运营工具,跨平台多账号一站式管理

下载
  • memory_order_acquire 保证其后所有读操作不被提前到该原子读之前
  • memory_order_release 保证其前所有写操作不被推迟到该原子写之后
  • memory_order_acq_rel 同时具备两者特性,适用于 fetch_addcompare_exchange_weak 等读-改-写操作
  • 不要为了“看起来高级”而滥用 memory_order_consume,目前主流编译器基本无视它,行为等同于 acquire

compare_exchange_weak 和 compare_exchange_strong 的区别不只是性能

它们都做“比较并交换”,但 weak 版本允许伪失败(spurious failure):即使当前值等于期待值,也可能返回 false。这在某些架构(如 ARM、LL/SC 实现)上是硬件限制,不是 bug。而 strong 版本承诺只要值匹配就成功,代价是可能触发更重的指令序列或循环重试。

典型错误:写成 if (x.compare_exchange_weak(expected, desired)) { ... },期望一次判断搞定——但 weak 可能失败,必须放在循环里重试;而 strong 虽然不用循环,但在高竞争下反而可能比 weak 更慢。

  • 绝大多数无锁数据结构(栈、队列节点插入)都用 weak + 循环,因为伪失败概率低,且循环开销远小于强版本的潜在代价
  • 仅当逻辑上“只尝试一次”有意义时才用 strong,比如初始化单例指针,失败就放弃而不是重试
  • 注意 expected 参数是引用,调用后若失败会被更新为当前实际值,这是为了方便下次循环直接复用

std::atomic 不能替代 mutex,尤其涉及多个变量协同时

它只能保证单个对象的读写原子性。两个 std::atomic<int></int> 变量之间没有事务性,无法实现“一起更新”或“原子地读取二者”。试图用多个原子操作拼出临界区,极易掉进 ABA 问题或状态撕裂陷阱。

比如实现一个带版本号的指针容器,只用 std::atomic<t></t>std::atomic<int></int> 分别存指针和版本,compare_exchange_weak 时无法同时验证二者——这就是经典的 ABA 场景,必须用 std::atomic<:pair int>></:pair>(需满足 trivially copyable)或更稳妥的 std::atomic<uint64_t></uint64_t> 手动打包。

  • 跨多个原子变量的逻辑一致性,优先考虑 std::mutex,无锁只是特定场景的优化手段,不是银弹
  • std::atomic_flag 是唯一保证 lock-free 的类型,适合实现自旋锁基元,但它只有 test_and_setclear,功能极简
  • 调试困难:无锁代码出问题往往表现为偶发、难以复现的数据错乱,gdb 断点可能干扰时序,导致问题消失

真正难的从来不是写对一个 fetch_sub,而是理清所有共享状态的修改顺序、依赖关系和内存序边界。这些东西没法靠文档速查,得一行行看汇编、用 tsan 跑压力测试,或者干脆先用 mutex 写通再逐步替换。

相关文章

编程速学教程(入门课程)
编程速学教程(入门课程)

编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

806

2023.08.22

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

540

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

26

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

38

2026.01.06

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

411

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

587

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

653

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

325

2025.12.24

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

4

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 9.3万人学习

C 教程
C 教程

共75课时 | 4.7万人学习

C++教程
C++教程

共115课时 | 17.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号