文章目录
CAS
1 什么是CAS?
CAS(Compare-and-Swap)是一种原子操作。
用处:用于在多线程编程中实现无锁(Lock-Free)数据结构。
核心功能是:比较内存中的当前值与预期值,如果两者相同,则将内存值更新为新值;否则不进行修改。整个过程是原子的,即不可被中断。
2 工作原理
CAS操作包含三个参数:
- 内存地址(Mem):需要修改的内存位置。
- 预期值(Expected):操作前读取的旧值。
- 新值(New):希望设置的新值。
执行流程:
- 读取内存地址的当前值。
- 比较当前值与预期值:
- 相等:将内存值更新为新值,返回成功。
- 不等:不修改内存,返回失败。
整个过程在硬件层面保证原子性,避免多线程竞争导致的数据不一致。
3 伪代码实现
function CAS(Mem, Expected, New):
if *Mem == Expected:
*Mem = New
return true
else:
return false
4 代码示例
在C++中,CAS通过 std::atomic
类型的 compare_exchange_weak
或 compare_exchange_strong
实现:
#include <atomic>
#include <iostream>
std::atomic<int> counter(0);
void increment_counter() {
int old_val = counter.load();
while (!counter.compare_exchange_weak(old_val, old_val + 1)) {
// CAS失败(old_val已过时),重新加载最新值并重试
}
}
int main() {
increment_counter();
std::cout << "Counter: " << counter << std::endl; // 输出 1
return 0;
}
5 CAS的应用场景
5.1 无锁数据结构
- 无锁队列:通过CAS实现线程安全的入队(Enqueue)和出队(Dequeue)。
- 无锁栈:使用CAS更新栈顶指针。
5.2 同步原语
- 自旋锁(SpinLock):线程通过CAS竞争锁。
class SpinLock { std::atomic<bool> locked{false}; public: void lock() { while (locked.exchange(true)) {} // CAS循环 } void unlock() { locked.store(false); } };
5.3 计数器与资源管理
- 原子计数器:如统计请求次数,避免锁争用。
6 CAS的ABA问题及解决方案
6.1 ABA问题描述
- 场景:线程A读取内存值为A,准备修改为C。此时线程B将A→B→A,线程A的CAS仍会成功,但实际中间状态已被改变。
- 风险:可能导致逻辑错误(如链表节点被释放后重用)。
6.2 解决方案
- 标签指针(Tagged Pointer):在指针高位增加版本号,每次修改递增。
struct TaggedPointer { void* ptr; uint64_t tag; // 版本号 }; std::atomic<TaggedPointer> atomic_ptr;
- 双字CAS(DCAS):扩展CAS操作位数,同时修改值和版本号(需硬件支持,如x86的
CMPXCHG16B
)。
7 CAS在不同语言中的实现
语言 | 实现方式 | 示例 |
---|---|---|
C++ | std::atomic::compare_exchange_weak | atomic_val.compare_exchange_weak(old, new) |
Java | AtomicInteger.compareAndSet() | atomicInt.compareAndSet(old, new) |
Go | atomic.CompareAndSwapInt32() | atomic.CompareAndSwapInt32(&val, old, new) |
8 硬件支持
- x86/x64:
CMPXCHG
(单字)和CMPXCHG16B
(双字)指令。 - ARM:
LDREX
(加载独占)和STREX
(存储独占)指令组合。 - RISC-V:
LR.W
(加载保留)和SC.W
(条件存储)指令。
9 CAS的优缺点
优点 | 缺点 |
---|---|
无锁,减少线程阻塞 | 需处理ABA问题 |
高并发性能 | 实现复杂,需循环重试(忙等待) |
避免死锁 | 不适用于所有场景(如复杂事务) |
10 总结
- CAS是并发编程的基石,通过原子比较与交换实现高效的无锁操作。
- 核心优势在于避免锁竞争,提升多线程性能,但需谨慎处理ABA问题。
- 合理应用于计数器、无锁队列等场景,可显著优化高并发系统性能。