为什么 atomic 提供 compare_exchange_strong?

Why atomic provide the compare_exchange_strong?

现在我正在研究 Android 上的 AtomicInteger class。

这个Javaclass有两个方法

public final void set(int newValue) {
    value = newValue;
}

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

问题一: set方法直接存储新值,所以这个方法不是原子的?如果我们希望结果在许多线程中都是正确的,我们需要使用 compareAndSet 方法?

我已经阅读了有关 AtomicInteger.compareAndSet() 的源代码。最后它调用 std::atomic<T>::compare_exchange_strong 方法。 incrementAndGet 方法调用 compareAndSet。

问题二: 我读过一些关于 CAS 的文章,compare_exchange_strong() 是原子的,store() 也是原子的。所以我想知道,为什么我们不直接使用 store,为什么需要 CAS 方法?

CAS 的要点在于它允许您存储 条件 原始值的值。一个简单的store当然可以原子地完成,但它可能不是你想要的。

考虑简单的 "atomic increment" 操作:

  1. 加载当前值。
  2. 计算新值。
  3. 存储新值。

如果您为此过程使用不相关的原子加载和存储,那么两个线程可以读取、计算和存储相同的值,因此其中一个增量会丢失!加载和存储是简单的原子操作,而 CAS 是原子 read-modify-write (RMW) 操作。 RMW 比简单的加载和存储更复杂。 RMW 不仅保证正确生成简单的值,而且保证正确应用复杂的逻辑操作。

让我们使用 CAS 实现原子增量来演示其工作原理:

// Effect: x += n, returns old value of x.
int inc(int n, std::atomic<int> & x)
{
    int old_val = x.load();
    for (;;) {
        int new_val = old_val + n;
        if (x.compare_exchange_weak(old_val, new_val)) {
            return old_val;
        }
        // Note: If the exchange fails, old_val is updated
        //       to the current value of x.
    }
}

这里的关键点是这个操作必须是一个循环,因为当我们计算我们的暂定结果(new_val = old_val + n)时,值old_val可能已经过时了,因为其他线程已经修改了它。所以我们必须循环 for 直到我们有机会在 x 当前持有旧值的情况下应用新值。这就是 CAS 的要点:它存储一个新值,条件是旧值是我们认为的。

关于"strong"与"weak"交换的区别:如果你希望你的算法无条件成功,你总是想要一个像我展示的循环,你使用弱交换。不同之处在于弱形式可能 "fail spuriously",即 return false,即使存储的值是预期值。 (这种放宽允许在某些平台上执行更有效的指令。)强形式可能更昂贵但不会虚假地失败,并且在您执行 "optimistic" CAS 但不关心是否它成功了。这在某些并发数据结构(例如队列)中很有用,在这些结构中,线程会尽最大努力提供帮助,但不需要保证成功。