关于atomic问题的一点理解

本文探讨了使用AtomicLong实现无锁同步计数器的方法。通过分析AtomicLong的compareAndSet方法,确保了在多线程环境下对计数器进行递增或重置操作的一致性和原子性。
之前看到一个帖子是关于atomic使用的,当时没有仔细分析问题,理解有误,作者回信给我,我又去看了一下.原来的问题大致如下:
private AtomicLong longValue = new AtomicLong(0);
if(longValue.incrementAndGet() > 50) {
longValue = new AtomicLong(0);
}

这样的写法能保证同步吗?按照这个写法的话,if块一定要使用同步锁保证同步的.但是那样的话Atomic的优势将大打折扣,代码的主要问题不在if而在if内的那句赋值:longValue = new AtomicLong(0).
重新new出Atomic实例+同步锁的双重成本,可能会使代码执行的性能不尽如人意.所以我昨天想了一下,要保证同步又不使用锁其实利用Atomic的CAS应该是能做到的.写了一个小例子:
public class AtomicCounterSample {
private final AtomicLong longValue = new AtomicLong(0);

public void incrementOrClear() {

// String name = Thread.currentThread().getName();

long var = longValue.get();
if(var >= 50) {
longValue.compareAndSet(var,0); // 1
}
else {
// System.out.println(name + ": " + var);

longValue.compareAndSet(var,var + 1l); // 2
}
}

public static void main(String[] args) {
final AtomicCounterSample sample = new AtomicCounterSample();

Thread threada = new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 100; i++) {
sample.incrementOrClear();
}
}
});
threada.setName("A");

Thread threadb = new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 150; i++) {
sample.incrementOrClear();
}
}
});
threadb.setName("B");

threada.start();
threadb.start();
}
}

这里我把AtomicLong的实例定义成final的,避免了实例化的开销.incrementOrClear()方法的作用就是递增或者清零.首先从理论上的保证是所有对AtomicLong的实例longValue的操作都是CAS的,所以其不会出现重复赋值的现象.这里我开了两个测试线程,循环调用incrementOrClear()方法,两个线程在方法内1,2处的情况分析如下:

1.线程A,B都处于2处,有两个可能:(1)线程A,B的局部变量var相同,这时候有一个线程的CAS成功,另一个的var就会与longValue的最新值不同,CAS失败,保证了不重复写.(2)线程A,B的var不同,例如线程A的var=7之后A暂缓,B线程一直CAS直到其var=30时A继续执行,A,B同时进入2.这个时候线程A的CAS显然要失败,因为longValue的最新值是30,而B的CAS成功,保证了longValue的递增,同时A再次调用方法时,var的值被同步到最新的值.

2.线程A,B都处于1处,这是只有一种可能就是两者的var都是50,此时也是只有一个线程能够成功的执行longValue的CAS操作使其清零.

3.线程A,B分布在1,2两处.那么拥有与longValue的最新值一致的var会成功执行CAS操作.假设A在1处,B在2处.A的var=50,B的var=49.若longValue的最新值是50则清零成功.若longValue的最新值是49,这种情况是A的var=50时被暂缓了,B执行了清零并且新循环到49.这个时候依然是B的CAS成功,A的清零失败.即使此时B被暂缓了,那么A也会失败之后再次调用方法更新var的值替代B执行递增.如果恰好循环到B暂缓时的var值时B也继续执行了,那么就是分析1的情况(1).

所以上述的分析,我认为任何情况都包括在内了,能够保证无锁机制的同步.并且实际运行也能体现了特别是3的分析:线程的,与longValue最新值不同的,var值再次打印时会被刷新而不是递增破坏longValue的唯一性.通过这个机会再次认识Atomic的CAS,我想还是有必要记录一下的.在此感谢作者的回复.
<think>嗯,用户正在深入钻研Linux内核的原子操作,特别是atomic_cmpxchg系列函数的使用方法。从引用内容看,用户已经查阅了相当底层的实现细节(比如x86的cmpxchg指令和ARM的ldxr/stxr),这应该是个内核开发者或系统级程序员。 用户的核心诉求很明确:要atomic_cmpxchg及其相关函数的实战指南。但有趣的是,ta提供的引用材料跨度很大——既有最基础的atomic_t结构定义[^4],又有arm64架构的独占加载指令细节[^2]。这说明ta可能需要从概念到实现的完整知识链。 注意到上次对话历史里我做过快速排序的示例,但当前问题和之前完全无关。用户特意强调“参考站内引用”,说明这些引用片段是ta精心筛选的,其中三个关键点值得重点回应: 1. atomic_cmpxchg的本质是CAS操作(引用[1]清晰展示了x86实现) 2. 不同架构的差异(引用[2]提到ARM的acquire/release语义) 3. 相关函数族的存在(如atomic_dec_return在引用[3]) 用户隐藏的深层需求可能是想解决实际开发中的并发竞争问题。ta没明说应用场景,但既然提到“相关函数”,应该需要知道何时选择cmpxchg而非其他原子操作。比如自旋锁实现常用cmpxchg,而引用计数可能用atomic_dec_return更合适。 在构建回答时,考虑采用“概念-接口-实战-架构差异”四层结构: 第一块用CAS伪代码解释原理,接着用struct kref示例展示典型应用场景。特别注意区分用户引用中提到的x86和ARM差异(比如ARM的ldaxr/stlxr指令流),最后总结函数选择决策树。所有代码示例保持与引用[1][2]的指令级实现呼应。 需要警惕的陷阱是内存顺序语义。用户引用里提到acquire/release变体(atomic_long_try_cmpxchg_acquire),这涉及ARM弱内存模型特性,必须在注意事项里强调。另外atomic_t在64位系统可能被atomic64_t替代的事实也要说明——虽然用户没直接问,但引用[2]提到atomic64_cmpxchg暗示了这一点。 最后生成的问题特意设计了难度梯度:首个问题帮助新手理解CAS价值,中间问题针对具体API使用陷阱,最后引导到性能调优层面。这样无论用户是初学者还是资深开发者都能找到切入点。 </think>以下是关于 `atomic_cmpxchg` 及相关函数的使用指南,结合 Linux 内核原子操作的实现原理和应用场景进行说明: --- ### **1. `atomic_cmpxchg` 核心功能** - **作用**:实现原子级的**比较并交换**(Compare-and-Swap, CAS)操作。 - **伪代码逻辑**: ```c bool atomic_cmpxchg(atomic_t *v, int old, int new) { if (v->counter == old) { v->counter = new; return true; } return false; } ``` - **返回值**:返回变量修改前的旧值(非布尔值),需通过比较判断是否成功: ```c if (atomic_cmpxchg(&v, expected_old, new) == expected_old) { // 修改成功 } ``` --- ### **2. 关键函数与使用场景** #### **(1) 基础原子操作** | **函数** | **作用** | **示例** | |---------------------------|-----------------------------|-----------------------------------| | `atomic_read(v)` | 原子读值 | `int val = atomic_read(&counter)` | | `atomic_set(v, i)` | 原子设值 | `atomic_set(&counter, 10)` | | `atomic_add(i, v)` | 原子加 | `atomic_add(5, &counter)` | | `atomic_inc(v)` | 原子自增1 | `atomic_inc(&counter)` | | `atomic_dec(v)` | 原子自减1 | `atomic_dec(&counter)` | #### **(2) CAS 相关函数族** | **函数** | **架构支持** | **用途** | |-----------------------------------|------------------|----------------------------------------| | `atomic_cmpxchg(v, old, new)` | 通用 | 标准 CAS 操作 | | `atomic_try_cmpxchg(v, old, new)` | 新版本优化 | 更高效的 CAS,直接更新 `old` 为当前值 | | `atomic64_cmpxchg` | 64 位系统 | 操作 64 位原子变量 | | `arch_atomic_cmpxchg_acquire` | ARM 等弱内存模型 | 带 Acquire 内存屏障的 CAS | --- ### **3. 典型使用案例** #### **(1) 实现自旋锁** ```c void spin_lock(atomic_t *lock) { while (atomic_cmpxchg(lock, 0, 1) != 0) cpu_relax(); // 等待锁释放 } ``` - **逻辑**:当锁值为 `0`(未锁定)时,将其设为 `1`(锁定);否则循环等待。 #### **(2) 安全修改共享资源** ```c void update_shared(atomic_t *v) { int old, new; do { old = atomic_read(v); new = calculate_new(old); // 基于旧值计算新值 } while (atomic_cmpxchg(v, old, new) != old); // 确保旧值未变化 } ``` - **关键**:避免在计算 `new` 时,其他线程修改 `v` 的值。 #### **(3) 引用计数清零** ```c bool try_release(atomic_t *ref) { int old = atomic_read(ref); if (old == 0) return false; // 已释放 // 尝试将引用计数从 1 减到 0 return atomic_cmpxchg(ref, 1, 0) == 1; } ``` --- ### **4. 内存顺序语义** 不同架构需关注内存屏障: - **x86**:`cmpxchg` 自带全屏障,无需额外操作。 - **ARM**:使用 `_acquire`/`_release` 后缀明确屏障语义[^2]: ```c // ARM64 实现(带 Acquire 屏障) static inline long arch_atomic_cmpxchg_acquire(atomic_t *v, long old, long new) { return __cmpxchg_case_acq(&v->counter, old, new, sizeof(int)); } ``` --- ### **5. 注意事项** 1. **数据类型匹配**: - 32 位变量用 `atomic_t` - 64 位变量用 `atomic64_t` 2. **错误用法**: ```c // 错误!cmpxchg 可能失败,需循环重试 if (condition) atomic_cmpxchg(v, old, new); ``` 3. **性能考量**:高竞争场景中,循环 CAS 可能导致 CPU 空转,需结合退避策略。 --- ### **6. 相关函数扩展** - **`atomic_xchg(v, new)`** 直接交换值,返回旧值: ```c int prev = atomic_xchg(&v, new); // 相当于 v->counter = new 的原子版本 ``` - **`atomic_dec_return(v)`** 自减后返回新值(引用计数常用)[^3]: ```c if (atomic_dec_return(&ref) == 0) kfree(obj); ``` --- ### **总结** - **核心原则**:CAS 是构建无锁算法的基石,需确保**“读-计算-写”** 的原子性。 - **选择函数**: - 通用场景 → `atomic_cmpxchg()` - 64 位系统 → `atomic64_cmpxchg()` - 弱内存架构 → 带屏障版本(如 `_acquire`) - **典型模式**:循环重试直到 CAS 成功。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值