1. 为什么需要无锁数据结构
在并发编程的世界里,锁就像十字路口的红绿灯,虽然能维持秩序但也不可避免地造成等待。我十年前第一次用mutex实现线程安全队列时,就发现当线程竞争激烈时,程序性能会断崖式下降。这就是为什么我们需要探索无锁(lock-free)编程的可能性。
原子操作是实现无锁数据结构的基石。想象两个线程同时修改同一个变量,普通操作就像两个人在同一本账本上随意涂改,而原子操作则像银行柜台交易——要么完整执行,要么完全不执行。C++11引入的<atomic>头文件为我们提供了这种"线程安全的变量修改"能力。
注意:无锁(lock-free)并不意味着完全不用锁,而是指至少有一个线程能够保证在有限步骤内完成操作,不会因为其他线程的挂起而被阻塞。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 原子操作基础解析
2.1 原子类型与内存顺序
C++中最简单的原子变量声明如下:
cpp复制std::atomic<int> counter(0);
这个counter的所有操作都是原子的。但原子性只是故事的一半,另一个关键概念是内存顺序(memory order),它决定了操作对其他线程的可见性顺序。
内存顺序主要有以下几种:
memory_order_relaxed:只保证原子性,不保证顺序memory_order_acquire:本线程后续读操作必须在本操作之后执行memory_order_release:本线程之前的写操作必须在本操作之前完成memory_order_seq_cst(默认):完全顺序一致性,性能开销最大
cpp复制// 典型的使用场景
std::atomic<bool> ready(false);
int data = 0;
// 线程1
data = 42; // (1)
ready.store(true, std::memory_order_release); // (2)
// 线程2
while(!ready.load(std::memory_order_acquire)); // (3)
assert(data == 42); // (4)
这里release-acquire配对保证了(1) happens-before (4)。
