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; ?>
】
相关推荐
- 跨文件调用类方法怎么用_php作用域操作符与自动加
- 如何使用Golang实现Web表单数据绑定_自动映
- Mac如何修改Hosts文件?(本地开发与屏蔽网站
- 如何在 VS Code 中正确配置并使用 NumP
- 如何在 Go 中创建包含 map 的 slice(
- php订单日志怎么在swoole写_php协程sw
- C++如何将C风格字符串(char*)转换为std
- Win11怎么设置虚拟桌面 Win11新建多桌面切
- php报错怎么查看_定位PHP致命错误与警告的方法
- Win11右键反应慢怎么办 Win11优化右键菜单
- 使用类变量定义字符串常量时的类型安全最佳实践
- PythonPandas数据分析项目教程_时间序列
- Win11怎么关闭开机声音_Win11系统启动提示
- Win11怎么设置单手模式_Win11触控键盘布局
- 如何在Golang中使用内置函数_Golangle
- Win11快速助手怎么用_Win11远程协助连接教
- 如何在 Django 中安全修改用户密码而不使会话
- Win11输入法选字框不见了怎么办_Win11输入
- Django 密码修改后会话失效的解决方案
- 如何在 Go 后端安全获取并验证前端存储的 JWT
- c++ try_emplace用法_c++ map
- MAC如何快速搜索大文件_MAC磁盘空间分析与冗余
- 如何在Golang中编写异步函数测试_Golang
- Win11关机界面怎么改_Win11自定义关机画面
- PHP怎么接收URL中的锚点参数_获取#后面参数值
- Win11怎么更改默认打开方式_Win11关联文件
- Mac如何整理桌面文件_Mac使用堆栈功能一键整理
- Win11怎么卸载Photos应用_Win11卸载
- Win10闹钟铃声怎么自定义 Win10闹钟自定义
- c++ stringstream用法详解_c++字
- Win11怎么开启空间音效_Windows11耳机
- Win10怎样清理C盘Steam游戏缓存_Win1
- 如何使用Golang安装依赖库_管理模块和第三方包
- Win11声音太小怎么办_Windows 11开启
- 如何使用 Selenium 正确获取篮球参考网站球
- Win11怎么关闭防火墙通知_屏蔽Win11安全中
- 本地php环境出现502错误_nginx或apac
- Win11怎么关闭搜索历史 Win11清除搜索框最
- Win11笔记本怎么看电池健康度_Win11电池报
- c++怎么操作redis数据库_c++ hired
- Windows10无法连接到Internet_Wi
- c# Task.Yield 的作用是什么 它和Ta
- Win11更新后变慢怎么办_Win11系统更新后卡
- win11如何清理传递优化文件 Win11为C盘瘦
- Win10怎么更改用户名 Win10修改账户名称操
- C++中的协变与逆变是什么?C++函数指针与返回类
- 如何高效删除 NumPy 二维数组中所有元素相同的
- 如何在Windows中创建新的用户账户?(标准与管
- Mac上的iMovie如何剪辑视频?(新手入门教程
- 微信JSAPI支付回调PHP怎么接收_处理JSAP

QQ客服