std::atomic提供无锁线程同步,通过原子操作如load、store、CAS及内存序控制实现高效并发,适用于计数器、栈等场景,但需注意ABA问题与内存回收风险。

在C++11中,std::atomic 提供了一种类型安全的方式来执行原子操作,避免使用互斥锁实现线程同步,从而提升性能。无锁编程(lock-free programming)利用原子操作保证多线程环境下数据的一致性和可见性,适用于高并发场景。
原子操作的基本概念
原子操作是指不可被中断的操作,要么完全执行,要么不执行,不会出现中间状态。在多线程环境中,对共享变量的读写可能引发竞态条件(race condition),传统做法是用互斥锁保护临界区。而使用 std::atomic 可以在不加锁的情况下安全地访问共享数据。
并不是所有类型都支持原子操作,std::atomic 支持如 int、bool、指针等基础类型。例如:
std::atomiccounter{0}; std::atomic ready{false}; std::atomic ptr;
常见原子操作与内存序
std::atomic 支持多种操作,包括 load、store、fetch_add、exchange、compare_exchange_weak/strong 等。这些操作可以配合不同的内存顺序(memory order)来控制同步强度和性能。
立即学习“C++免费学习笔记(深入)”;
常用的内存序包括:
- memory_order_relaxed:仅保证原子性,不提供同步或顺序约束
- memory_order_acquire:用于读操作,确保之后的读写不会被重排到该操作之前
- memory_order_release:用于写操作,确保之前的读写不会被重排到该操作之后
- memory_order_acq_rel:结合 acquire 和 release
- memory_order_seq_cst:最严格的顺序一致性,默认选项
示例:递增计数器
std::atomiccount{0}; void increment() { count.fetch_add(1, std::memory_order_relaxed); }
使用 compare-and-swap 实现无锁结构
最强大的原子操作之一是 compare_exchange_weak 和 compare_exchange_strong,它们实现了“比较并交换”(CAS)逻辑,是构建无锁数据结构的基础。
典型用法:
std::atomicvalue{0}; int expected = value.load(); while (!value.compare_exchange_weak(expected, expected + 1)) { // 如果 value 不等于 expected,compare_exchange_weak 会把当前值写入 expected // 并返回 false,循环继续尝试 }
这段代码实现了无锁的自增操作。即使多个线程同时执行,也能保证最终结果正确。
简单的无锁栈实现
下面是一个基于链表的无锁栈示例,展示如何使用原子指针和 CAS 构建线程安全的数据结构:
templatestruct lock_free_stack { struct node { T data; node* next; node(T const& d) : data(d), next(nullptr) {} }; std::atomiczuojiankuohaophpcnnode*youjiankuohaophpcn head{nullptr}; void push(T const& data) { node* new_node = new node(data); new_node-youjiankuohaophpcnnext = head.load(); while (!head.compare_exchange_weak(new_node-youjiankuohaophpcnnext, new_node)) { // 如果 head 被其他线程修改,new_node-youjiankuohaophpcnnext 会被更新为最新值 // 继续尝试直到成功 } } T pop() { node* old_head = head.load(); while (old_head && !head.compare_exchange_weak(old_head, old_head-youjiankuohaophpcnnext)) { // 尝试将 head 指向下一个节点 } if (old_head) { T result = old_head-youjiankuohaophpcndata; delete old_head; return result; } throw std::runtime_error("empty stack"); }};
注意:这个简单实现存在 ABA 问题和内存回收风险,在生产环境中需结合 hazard pointer 或 epoch-based 回收机制。
基本上就这些。合理使用 std::atomic 能有效减少锁竞争,提高并发性能,但需要仔细处理内存序和异常情况。无锁编程虽高效,但也更复杂,建议在真正需要性能优化时再采用。











