C++11+ 原子操作 `std::atomic`,现代并发编程的核心
之前我研究了机器人开发中的 ROS2(Jazzy)系统相关内容。并将官网中比较重要的教程和概念,按照自己的学习顺序翻译成了中文,并进行了整理和记录。到目前为止,已经整理了20多篇文章。如果你想回顾之前的内容,可以查阅主页中 ROS2(Jazzy)相关文章。
在研究 ROS2 的过程中,我发现它使用了不少 C++11 的新特性。这让我意识到,深入掌握这些特性对于深入理解 ROS2 的实现原理和优化代码非常重要。
因此,我萌生了撰写 C++11 系列文章的想法。目前已经完成了以下几篇文章:
- C++11 ROS2性能狂飙:C++11移动语义‘偷梁换柱’实战
- C++11 Lambda 表达式 以及
std::function
和std::bind
- C++11 智能指针:
std::unique_ptr
、std::shared_ptr
和std::weak_ptr
- C++11 的线程管理(
std::thread
)
本文是第五篇,主要总结的是 C++11 原子操作 std::atomic。
C++11 引入了 std::atomic
模板类,它提供了无锁线程安全操作的能力,允许在多线程环境中安全地访问和修改共享数据,而无需显式使用互斥锁。它是现代并发编程的核心组件。
一、原子操作的核心概念与原理
1. 为什么需要原子操作?
加入原子操作主要是为了解决以下问题和需求:
- 数据竞争问题:多线程同时读写同一个变量导致的未定义行为
- 锁的开销问题:互斥锁可能导致上下文切换、死锁、优先级反转等问题
- 性能需求:原子操作利用硬件指令实现高效的无锁同步
2. 原子操作的本质
- 不可分割性:操作要么完全执行,要么完全不执行
- 内存可见性:确保修改对所有线程立即可见
- 顺序约束:通过内存序控制指令重排序,这点在后面详细介绍。
二、基本用法与类型支持
1. 支持的类型
std::atomic
支持所有基本类型:
std::atomic<int> atomicInt(0); // 原子整型
std::atomic<bool> atomicFlag(false); // 原子布尔
std::atomic<double> atomicDouble; // 原子浮点(有限操作)
2. 常用的原子操作接口
// 存储值
atomicInt.store(42);// 读取值
int value = atomicInt.load();// 交换值
int old = atomicInt.exchange(100);// 比较交换(CAS)
bool success = atomicInt.compare_exchange_weak(expected, desired);
3. 在算术运算(整数类型)中使用原子操作
std::atomic<int> counter(0);counter.fetch_add(1); // 原子加(返回旧值)
counter.fetch_sub(1); // 原子减
counter++; // 等价于 fetch_add(1) + 1
++counter