c++ std::atomic如何保证原子性 c++ CAS操作原理【底层】
技术百科
穿越時空
发布时间:2026-01-01
浏览: 次 std::atomic的原子性由编译器生成特定CPU指令(如x86的lock、ARM的ldxr/stxr)与硬件共同实现,CAS是核心原语;compare_exchange_weak允许伪失败,compare_exchange_strong无伪失败;内存序控制可见性而非原子性。
std::atomic 的原子性不是靠 C++ 语言本身“保证”的,而是通过编译器生成特定的底层指令(如 x86 的 lock 前缀指令、ARM 的 ldxr/stxr 等)+ CPU 硬件支持共同实现的。CAS(Compare-And-Swap)是其中最核心的原语,它的原子性直接由 CPU 提供。
CPU 级别的原子操作支持
现代 CPU 在硬件层面提供了对单条内存访问指令的原子保障,但仅限于满足对齐、大小等条件的简单读写(如 4 字节对齐的 int)。而像“读-改-写”这类复合操作(例如 i++),天然不是原子的——中间可能被中断或并发修改。为此,CPU 提供了专门的原子指令:
- x86/x64:使用
lock cmpxchg指令实现 CAS;lock前缀会锁定总线或缓存行(取决于架构和操作数大小),阻止其他核心/线程在同一时间修改同一缓存行。 - ARM64:使用
ldxr(load-exclusive) +stxr(store-exclusive)配对。CPU 会标记某地址为“独占监视”,后续stxr成功当且仅当该地址未被其他核心修改过——失败则需重试。 - RISC-V:类似 ARM,用
lr.w/sc.w(load-reserved / store-conditional)机制。
std::atomic::compare_exchange_weak/strong 的底层行为
这两个函数最终映射到上述 CPU 指令。区别在于:
-
compare_exchange_weak允许“伪失败”(spurious failure):即使预期值匹配,也可能因底层机制(如 ARM 的独占监视被意外清除)返回 false。适合循环重试场景,性能略高。 -
compare_exchange_strong保证:只要预期值匹配,就一定成功(无伪失败)。但某些平台(如早期 ARM)需额外检查+重试模拟,开销稍大。
典型用法
是循环尝试:
while (!val.compare_exchange_weak(expected, desired)) {
// expected 已被更新为当前实际值
}
内存序(memory order)不是原子性,但影响可见性
原子性只解决“执行不中断”,不解决“结果何时对其他线程可见”。std::memory_order 控制编译器重排和 CPU 指令重排,以及缓存同步时机:
-
memory_order_relaxed:只保证该操作自身原子,不约束前后内存访问顺序,也不触发 cache coherency 同步(最快,适合计数器等无需同步语义的场景)。 -
memory_order_acquire(读)/memory_order_release(写):构成“获取-释放”同步,保证本线程中该操作前后的内存访问不会跨过它重排,并在多核间建立 happens-before 关系。 -
memory_order_seq_cst(默认):最强语义,所有线程看到的操作顺序一致,相当于全局顺序锁,性能开销最大。
编译器与运行时的协作
std::atomic 对象在编译期会被标记为不可分割访问(禁止拆解为多个 mov 指令),并根据模板参数类型选择对应宽度的原子指令(如 atomic → 32 位指令)。对于不支持原生原子操作的类型(如过大结构体),std::atomic 会退化为内部加锁(如 mutex),此时原子性由互斥量保证,而非硬件指令——但这已不属于“无锁原子操作”范畴。
# ai
# 这类
# 也不
# 多个
# 并在
# 已被
# 而非
# 这两个
# app
# 见性
# 循环
# 并发
# 对象
# c++
# int
# 字节
# 区别
# 线程
# 架构
# red
# 无锁
# 重试
# 结构体
# while
# 多核
# Conditional
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Mac怎么开启“任何来源”_Mac安装未签名应用的
- 如何在Golang中实现CI/CD流水线自动化测试
- Win11如何设置开机问候语 Win11修改登录界
- Python异步网络编程_aiohttp说明【指导
- c++怎么用jemalloc c++替换默认内存分
- php中$this和::能混用吗_对象与静态作用域
- Win11如何隐藏桌面图标 Win11一键隐藏/显
- 如何高效识别并拦截拼接式恶意域名 spam
- Mac怎么设置登录项_Mac管理开机自启动程序【教
- Win11怎么制作U盘启动盘_Win11原版系统安
- 如何使用Golang构建基础消息队列模拟_Gola
- Python字符串处理进阶_切片方法解析【指导】
- 如何在 Windows 11 中使用 AlomWa
- Mac电脑如何恢复出厂设置_Mac抹掉数据并重装系
- Win11蓝牙开关不见了怎么办_Win11蓝牙驱动
- Win11怎样安装企业微信_Win11安装企业微信
- Win11如何暂停系统更新 Win11暂停更新最长
- php怎么下载安装后设置默认字符集_utf8配置步
- 如何在 Pandas 中按元素交集合并两列字符串
- VSC怎样在VSC中调试PHPAPI_接口调试技巧
- Win11怎么查看显卡温度 Win11任务管理器查
- Win11怎么设置虚拟内存最佳大小_Windows
- php中self::能调用子类重写的方法吗_静态绑
- c++ unordered_map怎么用 c++哈
- 短链接怎么用php递归还原_多层加密链接的处理法【
- Win11怎么设置任务栏对齐方式_Windows1
- Go语言中正确反序列化多个同级XML元素为结构体切
- Mac如何与安卓手机传文件_Mac和Android
- 如何使用Golang配置安全开发环境_防止敏感信息
- c# 在ASP.NET Core中管理和取消后台任
- php报错怎么查看_定位PHP致命错误与警告的方法
- 如何使用Golang开发简单的聊天室消息存储_Go
- Python网络异常模拟_测试说明【指导】
- Mac怎么安装软件_Mac安装dmg与pkg文件的
- Win11怎么清理C盘OneDrive缓存_Win
- 如何使用Golang操作指针变量_Golang解引
- php接口返回数据乱码怎么办_php接口调试编码问
- Django 密码修改后会话失效的解决方案
- php打包exe后无法写入文件_权限问题解决方法【
- Win10系统映像怎么恢复 Win10使用系统映像
- Go语言中slice追加操作的底层共享机制详解
- Win10如何优化内存使用_Win10内存优化技巧
- Win10如何更改电脑休眠时间_Windows10
- Windows服务持续崩溃怎样修复_系统服务保护机
- C++如何使用std::async进行异步编程?(
- 一文教你快速开通网站LOGO图
- PHP怎么接收前端传的时间戳_处理时间戳参数转换技
- c# 在高并发下使用反射发射(Reflection
- Mac如何备份到iCloud_Mac桌面与文稿文件
- 如何在Golang中实现邮件发送功能_Golang

QQ客服