news 2026/5/5 3:08:52

C++笔记---并发支持库(atomic)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++笔记---并发支持库(atomic)

1. atomic

C++11 引入的<atomic>头文件和std::atomic模板是无锁并发编程的核心,用于实现多线程间的原子操作,避免数据竞争(data race),替代传统的互斥锁(如 std::mutex)以提升并发性能。

使用atomic的成员函数,对T类型的数据进行操作是原子的,这就使得某些简单的操作不再需要我们去加锁访问,而是直接采用原子操作。例如,一个多线程共享的计数器:

atomic<int> a_cnt = 0; int cnt = 0; void func() { for (int i = 0; i < 100000; i++) { ++a_cnt; ++cnt; } } int main() { vector<thread> pool; for (int i = 0; i < 4; i++) { pool.emplace_back(func); } for (auto& t : pool) { t.join(); } cout << "原子:" << a_cnt << endl; cout << "非原子:" << cnt << endl; return 0; }

值得注意的是,atomic作为模板,实际上并不完全支持所有类型。

主要支持的是整型家族、指针类型,以及任何满足 CopyConstructible 和 CopyAssignable 的可简单复制 (TriviallyCopyable)类型,例如:

struct Counters { int a; int b; }; // user-defined trivially-copyable type std::atomic<Counters> cnt; // specialization for the user-defined type

从C++20开始,atomic对智能指针进行了特化:

如果如下六个函数的返回值均为true,则说明类型 T可以使用原子操作,否则不行:

std::is_trivially_copyable<T>::value std::is_copy_constructible<T>::value std::is_move_constructible<T>::value std::is_copy_assignable<T>::value std::is_move_assignable<T>::value std::is_same<T, typename std::remove_cv<T>::type>::value

注意:std::atomic 对象不可拷贝、不可移动,因为拷贝 / 移动会破坏原子性

1.1 核心成员函数

函数功能
load原子读取值
store原子写入值
exchange原子交换值(返回旧值,写入新值)
compare_exchange_weak/strong比较并交换(CAS):核心原子操作,实现无锁算法的基础
fetch_add/fetch_sub原子加减(返回旧值),仅对整数 / 指针类型有效
operator++/--原子自增 / 自减(重载运算符,等价于fetch_add(1)/fetch_sub(1))
operator=原子赋值(等价于 store(val))
is_lock_free()判断当前原子操作是否 “无锁”(否则内部可能用互斥锁实现)

1.2 CAS操作

CAS 是无锁编程的基石,即Compare And SetCompare And Swap,上面所有对值进行修改的成员函数,底层都是通过如下两个函数实现:

bool compare_exchange_weak( T& expected, T desired) bool compare_exchange_strong( T& expected, T desired)

这两个函数均为原子操作,依赖于硬件提供的CAS指令,核心原理为:

比较原子对象的当前值expected

  • 若相等:将原子对象值设为desired,返回true
  • 若不等:将expected更新为原子对象的当前值,返回false

weak 与 strong 的区别在于是否使用缓存一致性协议

  • weak:弱版本,可能 “伪失败”(值相等但返回 false),性能更高;
  • strong:强版本,值相等时必成功,无伪失败。

例如,operator++的底层实现可能与Add函数的实现相似:

atomic<int> a_cnt = 0; int cnt = 0; void Add(atomic<int>& cnt) { int old = cnt.load(); // cnt与old的值相同,则将new赋值给cnt,否则将cnt的值更新给old // 确保将数据写回之前,没有其他线程对目标数据进行了修改,进而导致数据的覆盖 // 本质上来说,atomic的原理就是在将数据写回之前验证数据是否已被其他线程修改 // 若已被修改则重新计算更新后的值,并再次尝试写回,直到某次成功 // while (!atomic_compare_exchange_weak(&cnt, &old, old + 1)); while (!a_cnt.compare_exchange_weak(old, old + 1)); } void func() { for (int i = 0; i < 100000; i++) { Add(a_cnt); ++cnt; } } int main() { vector<thread> pool; for (int i = 0; i < 4; i++) { pool.emplace_back(func); } for (auto& t : pool) { t.join(); } cout << "原子:" << a_cnt << endl; cout << "非原子:" << cnt << endl; return 0; }

再例如,使用CAS操作实现无锁的链式栈(部分代码):

#pragma once #include <atomic> template <typename T> class Node { int _val; Node* _next; Node(int val = 0, Node* next = nullptr) :_val(val) ,_next(next) { } }; template <typename T> class LockFreeStack { public: void push(const T& val) { Node<T>* newNode = new Node<T>(val, _head.load()); while (!_head.compare_exchange_weak(newNode->next, newNode)); } private: std::atomic<Node<T>*> _head = nullptr; };

1.3 内存序(Memory Order)

std::atomic 的所有操作都可指定内存序参数(默认 std::memory_order_seq_cst),用于控制:

  • 指令重排序:编译器 / CPU 是否会重排原子操作的前后指令;
  • 内存可见性:一个线程的写操作对另一个线程的读操作的可见性。

例如:

内存序枚举值含义
memory_order_relaxed松散序仅保证操作本身原子性,无可见性 / 重排序约束(最弱)
memory_order_consume消费序:保证对依赖于该原子操作的读写不重排(C++20 已弃用)
memory_order_acquire获取序读操作,禁止后续指令重排到该操作前,且能看到之前的释放操作
memory_order_release释放序写操作,禁止之前指令重排到该操作后,且写结果对获取序可见
memory_order_acq_rel同时具备 acquire 和 release 语义(用于读写操作,如 CAS)
memory_order_seq_cst顺序一致序:所有线程看到的操作顺序一致(最强,默认,性能最差

通常来说使用默认内存序即可,各内存序的效率差别实际上并不大,优先保证正确性。在要求极致性能的场景下,我们再考虑对内存序进行优化。

2. 原子操作实现自旋锁

#pragma once #include <atomic> class SpinLock1 { public: void lock() { // exchange: 将对象值设置为参数值,返回原本的值 while (_flag.exchange(true)) { // 自旋等待锁释放 } } void unlock() { _flag.store(false); } private: std::atomic<bool> _flag = false; }; class SpinLock2 { public: void lock() { while (_flag.test_and_set()) { // 自旋等待锁释放 } } void unlock() { _flag.clear(); } private: std::atomic_flag _flag = ATOMIC_FLAG_INIT; };

3. 无锁队列

未来补充。。。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/2 12:37:57

游戏开发者必看:彻底解决msvcp100.dll报错

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个游戏运行环境检测工具&#xff0c;专门针对msvcp100.dll问题。功能包括&#xff1a;1. 游戏启动前自动检查运行库&#xff1b;2. 可视化展示缺失的DLL文件&#xff1b;3. 内…

作者头像 李华
网站建设 2026/5/3 20:39:29

73、Linux系统性能监控与并行执行技术解析

Linux系统性能监控与并行执行技术解析 在Linux系统环境下,尤其是涉及到RAC(Real Application Clusters)集群时,性能监控和并行执行是保障系统高效运行的关键技术。下面我们将详细介绍一些常用的性能监控工具以及Oracle数据库中的并行执行技术。 性能监控工具 sar工具及结…

作者头像 李华
网站建设 2026/4/21 7:10:58

KDDockWidgets终极指南:打造专业级Qt停靠界面

想要为你的Qt应用程序打造功能强大、体验流畅的停靠界面吗&#xff1f;KDDockWidgets作为KDAB开发的先进停靠小部件库&#xff0c;提供了远超Qt原生QDockWidget的专业级功能。本文将为新手开发者提供完整的快速上手方案&#xff0c;帮助你轻松掌握这个强大的框架。 【免费下载链…

作者头像 李华
网站建设 2026/4/28 18:12:21

uni-app插件市场实战指南:从零构建多端应用的高效路径

uni-app插件市场实战指南&#xff1a;从零构建多端应用的高效路径 【免费下载链接】uni-app A cross-platform framework using Vue.js 项目地址: https://gitcode.com/dcloud/uni-app 在当今快节奏的移动互联网时代&#xff0c;如何快速开发出适配多个平台的应用程序已…

作者头像 李华
网站建设 2026/5/3 19:22:00

77、深入解析 Oracle 并行执行与升级到 11g Release 2

深入解析 Oracle 并行执行与升级到 11g Release 2 1. 并行执行环境中的互连吞吐量 在并行执行环境里,互连起着至关重要的作用,特别是在集群中不同节点的生产者和消费者之间传输数据时。AWR 报告包含了按客户端划分的互连吞吐量部分,展示了并行查询所使用的带宽水平。 下面…

作者头像 李华
网站建设 2026/5/1 8:21:59

78、升级到Oracle 11g Release 2的全面指南

升级到Oracle 11g Release 2的全面指南 1. 启动Oracle通用安装程序 当启动Oracle通用安装程序时,系统会进行一系列检查。例如,会检查交换空间,要求其必须大于500 MB。若实际交换空间为4095 MB,则检查通过。同时,会显示库存指针和库存的位置,如库存指针位于 /etc/oraIn…

作者头像 李华