【Effective Modern C++】第七章 并发API:40. 对于并发使用atomic,对于特殊内存使用volatile

📅 发布时间:2026/7/6 7:49:58 👁️ 浏览次数:
【Effective Modern C++】第七章 并发API:40. 对于并发使用atomic,对于特殊内存使用volatile
为什么要区分这两个关键字volatile之所以被拿来和并发编程混为一谈是因为其他语言Java/C#中volatile有并发语义部分 C 编译器给volatile加了非标准的并发特性但在标准 C 中volatile和并发毫无关系而开发者常把它和处理并发的std::atomic搞混这是文章要解决的核心问题。std::atomic专为并发设计的原子操作工具能力保证对变量的操作读、写、读 - 改 - 写是原子性的不可分割无需互斥锁底层通过专用机器指令实现比锁更高效。比如aiai是std::atomicint是完整的原子操作多线程执行时不会出现 “读 - 改 - 写” 拆分导致的错误对比普通变量 /volatile变量的自增会拆分成 “读值→加 1→写回” 三步多线程下会出现计数错误比如两次自增只得到 1 而非 2。内存序限制默认的 “顺序一致性” 模型会限制编译器 / 硬件的指令重排保证操作的执行顺序和代码逻辑一致。比如先计算imptValue再设置valAvailable truestd::atomic能保证其他线程不会看到valAvailable为true但imptValue未计算完成的情况这是并发通信的关键比如用标志位通知 “数据已就绪”。限制删除了拷贝构造 / 赋值函数避免非原子的拷贝操作破坏线程安全必须通过load()/store()读写值。volatile专为 “特殊内存” 设计的防优化工具能力告诉编译器 “这块内存是特殊的不要优化看似冗余的读写操作”仅针对内存优化和并发无关。冗余读auto y x; y x;x是volatile int编译器不会省略第二次读比如x是温度传感器的内存映射地址两次读取值可能不同冗余写x 10; x 20;编译器不会省略第一次写比如x是无线电控制端口两次赋值是不同指令。局限性无原子性保证volatile变量的自增、读写依然是拆分操作多线程下会出现数据竞争属于未定义行为无指令重排限制编译器 / 硬件仍可能重排volatile变量的操作顺序无法保证并发通信的正确性。std::atomicvsvolatile特性std::atomicvolatile目标保证多线程访问的原子性和内存序禁止编译器优化特殊内存的读写适用场景并发编程无锁多线程访问变量特殊内存内存映射 I/O、外设通信等原子性支持读、写、RMW 操作均原子不支持指令重排限制有默认顺序一致性无编译器优化允许比如合并多次load()为一次禁止必须保留所有读写特殊场景两者可以结合使用如果变量同时满足 “需要原子操作” 和 “需要防优化”比如内存映射 I/O 地址被多线程访问可以组合声明volatile std::atomicint vai;std::atomic保证操作的原子性volatile禁止编译器优化对这块特殊内存的读写。额外实践建议显式调用std::atomic的load()/store()函数虽然不是语法必需但能清晰标识变量是 “非普通” 的便于排查性能瓶颈原子操作比普通操作慢提醒开发者注意并发语义避免遗漏std::atomic声明导致的并发错误。总结std::atomic是并发编程工具解决多线程无锁访问变量的原子性和内存序问题和特殊内存无关volatile是特殊内存处理工具禁止编译器优化冗余读写和并发安全无关两者核心目标完全不同不可互相替代仅在 “特殊内存 多线程访问” 的极端场景下组合使用。原著在线阅读地址