C++里的volatile关键字在多线程中有效吗?(仅防止编译器优化,不保证原子性)
技术百科
冰火之心
发布时间:2026-01-20
浏览: 次 volatile不能替代原子操作或互斥锁,它仅禁用编译器优化,不保证内存可见性、不阻止CPU重排、不提供原子性;多线程中必须使用std::atomic。
volatile 不能替代原子操作或互斥锁
volatile 在 C++ 多线程中**完全不能保证线程安全**。它只告诉编译器:“这个变量可能被外部(如硬件、信号处理函数)修改,不要对它的读写做优化”,但对 CPU 指令重排、缓存一致性、内存可见性等多线程核心问题毫无约束。
常见错误现象:
两个线程分别对 volatile int flag = 0; 执行 flag = 1; 和 while(flag == 0) { },仍可能无限循环——因为写入未刷新到其他核的缓存,或读取被 CPU 乱序执行绕过。
-
volatile不生成内存屏障(std::atomic_thread_fence),不触发 cache coherency 协议(如 MESI)同步 - 不阻止 CPU 级重排:
volatile变量的读写仍可能被 CPU 与其他内存操作重排 - 不提供原子性:
volatile int x;的x++是“读-改-写”三步,非原子,多线程下必然竞态
什么场景下 volatile 还算有用
仅限于与硬件寄存器、信号处理函数(signal handler)或某些特殊嵌入式环境交互时,防止编译器把反复读写的变量优化成寄存器缓存。在标准多线程程序中,这些场景几乎不存在。
使用场景举例:
- 映射到内存的硬件状态寄存器(如
volatile uint32_t* const STATUS_REG = (uint32_t*)0x40001000;) - 被
signal()注册的异步信号处理函数修改的全局变量(需配合sig_atomic_t) - 某些裸机或 RTOS 中由中断服务程序更新的标志位
注意:volatile sig_atomic_t 是 POSIX 对信号

正确替代方案:用 std::atomic 替代 volatile
所有需要跨线程通信的变量,应无条件使用 std::atomic,它同时提供:编译器不优化 + CPU 内存序控制 + 原子操作语义。
std::atomicready{false}; // 线程1: ready.store(true, std::memory_order_release); // 线程2: while (!ready.load(std::memory_order_acquire)) { // 自旋等待 }
关键差异:
-
std::atomic默认使用std::memory_order_seq_cst,提供最强顺序保证;可按需降级为acquire/release提升性能 -
volatile无法指定内存序,也无法做 compare-exchange(compare_exchange_weak)等原子原语 - 即使简单布尔标志,也必须用
std::atomic_bool,而非volatile bool
编译器和 CPU 层面的真实限制
现代 x86 架构虽有较强的内存模型(写操作不会重排到写之后,读不会重排到读之前),但 volatile 仍不触发 mfence/lfence 指令;而 std::atomic 在需要时会插入对应指令或利用 LOCK 前缀。
在 ARM/AArch64 或 RISC-V 上问题更严重:
它们默认弱内存模型,volatile 完全无法阻止任意重排,std::atomic 则通过明确的 ldar/stlr 指令或 dmb ish 实现同步。
容易被忽略的一点:
即使你用 volatile 加了“看起来很谨慎”的注释,只要没用 std::atomic 或 std::mutex,代码在任何主流平台都属于未定义行为(UB),尤其开启 -O2 后,编译器可能彻底优化掉你的等待循环。
# 而非
# 要对
# 较强
# 见性
# 循环
# c++
# int
# 线程
# 架构
# signal
# 异步
# 信号处理
# volatile
# 多线程
# while
# const
# bool
# 布尔
# 全局变量
# 但对
# 互斥
# 还算
相关栏目:
<?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; ?>
】
相关推荐
- Win11怎么关闭右下角弹窗_Win11拦截系统通
- Win11 explorer.exe频繁崩溃_修复
- 如何在包含多值的列中精准搜索指定演员?
- Win10系统字体模糊怎么办_Windows10高
- Win11怎么设置虚拟内存_Windows 11优
- Win11怎么设置任务栏大小_Windows11注
- Windows怎样拦截QQ浏览器广告_Window
- Windows10如何更改桌面背景_Win10个性
- 如何使用Golang实现Web表单数据绑定_自动映
- Python函数参数高级用法_默认值与可变参数解析
- Win11怎样安装钉钉客户端_Win11安装钉钉教
- Linux怎么禁止Root用户远程登录_Linux
- 怎么将XML数据可视化 D3.js加载XML
- Drupal 中渲染节点时出现 HTML 标签嵌套
- Win11怎么关闭透明效果_Windows11个性
- 如何在Golang中实现WebSocket广播_使
- php嵌入式需要什么环境_搭建php+linux嵌
- Win11怎么用设置清理回收站_Win11设置清理
- Win11怎么设置右键刷新选项_Windows11
- Windows电脑如何截屏?(四种快捷方法)
- 如何高效识别并拦截拼接式恶意域名 spam
- 如何使用Golang实现基本类型比较_Golang
- php在Linux怎么部署_LNMP环境搭建PHP
- Mac的“调度中心”与“空间”怎么用_Mac多桌面
- Win11怎么调整屏幕亮度_Windows 11调
- Win11如何更改用户账户文件夹名称 Win11修
- PHP接收参数值为空怎么办_判断和处理空参数方法说
- Win11怎么关闭内容自适应亮度_Windows1
- 如何使用Golang匿名函数_快速定义临时函数逻辑
- Mac如何设置动态壁纸?(让桌面动起来)
- Python数据挖掘核心算法实践_聚类分类与特征工
- C++ static_cast和dynamic_c
- Win10怎样清理C盘阿里旺旺缓存_Win10清理
- Win11相机打不开提示错误怎么修_相机权限开启与
- php能控制zigbee模块吗_php通过串口与c
- Python字符串处理进阶_切片方法解析【指导】
- Win11怎么恢复出厂设置_Win11重置此电脑保
- c++怎么编写动态链接库dll_c++ __dec
- Python生成器表达式内存优化_惰性计算说明【指
- Windows7如何安装系统镜像_Windows7
- c++如何获取map中所有的键_C++遍历键值对提
- Windows10如何更改开机密码_Win10登录
- Win11怎么清理C盘系统日志_Win11清理系统
- mac本地php环境如何开启curl_curl扩展
- Windows服务启动类型恢复方法_错误修改导致的
- Win11怎么关闭自动更新 Win11永久关闭系统
- MAC如何启用访达侧边栏显示_MAC Finder
- php中作用域操作符能访问私有静态属性吗_访问权限
- Windows10系统怎么查看CPU温度_Win1
- Win11怎么恢复误删照片_Win11数据恢复工具

QQ客服