CMPXCHG和lwarx (LL)/stwcx (SC)

CMPXCHG 指令和 lwarx/stwcx(LL/SC)指令是在不同体系结构中常见的原子操作指令。

  1. CMPXCHG:
    • CMPXCHG 是 x86 架构中用于执行原子比较并交换操作的指令。这个指令会比较某个内存位置的值与累加器中的值,如果相等,则将累加器中的值赋给该内存位置,并且返回旧的内存位置的值。因此,CMPXCHG 通常用于实现原子的读-改-写操作。
  2. lwarx/stwcx (LL/SC):
    • lwarx/stwcx 是 PowerPC 架构中用于实现类似原子操作的指令。lwarx 用于加载一个字(word)的值到寄存器中,而 stwcx 用于尝试将新值存储回内存。如果在这次加载和存储之间没有其他处理器修改了这个内存位置,则存储操作成功,否则它失败。

ABA 问题

  • ABA 问题指的是一个共享内存区域中的值被修改两次,最终回到了原始的值,使得检查不到这个变化。这种情况可能会导致并发数据结构出现问题。
  • CMPXCHG 指令在解决ABA问题上存在缺陷,因为它只能检查共享内存区域的当前值是否等于预期值,但无法检查这个值是否“曾经”等于预期值。这就是 ABA 问题的根源。
  • 相比之下,lwarx/stwcx 指令通过使用 LL/SC 的方式,可以在加载和存储之间检测共享内存区域的变化情况,因此可以避免 ABA 问题的发生。

因此,lwarx/stwcx 指令能够提供更强大的原子性保证,从而避免了类似于 ABA 问题的并发风险。

有一种比较有效的做法是:将共享内存区域的修改与版本号或标记相关联。每次对共享内存的修改都会增加版本号,这样即使出现了ABA问题,由于版本号已经发生了改变,便可检测到这种情况。

一些库或语言提供了支持带有回退机制的CAS操作,例如 Java 中的 AtomicStampedReference 类。该类允许用户为引用的目标对象附加一个整数作为版本戳,并且在CAS操作失败时,可以检查目标对象是否还包含相同的版本戳。

下面这个是C++实现的带版本号的CAS
一种常见的方法是创建一个结构体,其中包含指向数据的指针以及版本号或标记,并使用原子操作来更新和比较这两个值。通过使用 std::atomic 来确保这些操作是原子的。

#include <atomic>

template <typename T, typename U>
struct StampedReference {
    T* pointer;
    U stamp;
};

// 使用StamedReference进行CAS操作
template <typename T, typename U>
bool atomicCompareAndSet(std::atomic<StampedReference<T, U>>& ref, T* expectedPointer, T* newPointer, U expectedStamp, U newStamp) {
    StampedReference<T, U> oldValue = ref.load();
    if (oldValue.pointer == expectedPointer && oldValue.stamp == expectedStamp) {
        StampedReference<T, U> newValue {newPointer, newStamp};
        return ref.compare_exchange_weak(oldValue, newValue);
    }
    return false;
}

在上面的示例中,我们创建了一个 StampedReference 结构体来存储指针和版本号,并编写了一个模板函数 atomicCompareAndSet 来执行CAS操作。当CAS操作失败时,可以检查版本戳并采取适当的行动。
CAS(Compare and Swap)通常与循环结合使用,是因为在并发环境下,多个线程尝试对同一内存位置执行 CAS 操作时可能会遇到竞争条件。如果不使用循环来重试 CAS 操作,那么在竞争激烈的情况下,单次 CAS 操作失败后,程序可能就会放弃修改,这样会导致并发操作的数据一致性出现问题。

通过在循环中重试 CAS 操作,可以确保在 CAS 操作成功之前不会跳出循环,从而保证了原子性和一致性。这种方法被称为自旋锁,因为线程会持续尝试 CAS 直到成功,而不是将自己挂起等待。

当使用 atomicCompareAndSet 这样的原子操作函数时,通常还需要将其放置在一个循环中以确保 CAS 操作的原子性。这种自旋方式会在多线程环境中保证正确的行为,并且能够有效地避免线程切换所带来的开销。

总之,即使使用了原子操作函数,也仍然需要搭配使用循环,以确保 CAS 操作的成功和数据的一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾牙慧者

欢迎请作者喝奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值