这里引用了本次 CppCon 讲座中 37:00 的幻灯片:https://youtu.be/DS2m7T6NKZQ
虽然演示者正在谈论 compare_exchange_swap_strong 和 compare_exchange_swap_weak 在虚假故障情况下的区别,但提供以下代码作为示例用例。这个问题可能有点无聊,因为它并不是真正的主要话题,但我试图更好地理解无锁算法。
bool expected = false;
extern atomic_bool b; // set somewhere else
while(!b.compare_exchange_weak(expected, true) && !expected);
我不明白 compare exchange swap 这里的用例。如果当我们进入 while 循环时原子布尔值 b 为假,compare_exchange_swap_weak 将返回真并设置 b 为真,然后我们跳出 while 循环(假设 cas_weak 在这里没有虚假地失败)。
如果我们进入 while 循环时原子布尔 b 为真,则 compare_exchange_swap_weak 将返回 false 并将 expected 设置为 true。然后 !expected 将为 false,我们跳出 while 循环。
但是,如果当我们进入 while 循环时 b 的值为 false,并且 cas 操作确实失败了,那么我们继续循环。
所以看起来这段代码的目的是将 b 的值设置为 true,如果它以 lock_free 方式为 false。除非我误解了什么,否则我们不能在实践中做类似 b.store(true) 的事情吗?
我认为这是一个
try_lock()
函数,其中b
是自旋锁。获取锁后退出,或在一次非虚假故障后退出。或者它是 compare_exchange_strong
的仿真(在这种情况下你应该使用它。)
为了塞进幻灯片而简化/压缩;在实际情况下,您可能会使用不同的代码来更轻松、更清楚地区分成功与非虚假失败!但我认为在这段代码中,我们可以返回
!expected
作为锁定成功状态。
extern atomic_bool b; // set somewhere else
bool expected = false;
while(!b.compare_exchange_weak(expected, true) && !expected);
三种情况是:
expected
的值。 while(false && ...)
.expected = true
。循环退出; while(!false && !true);
false
以外的值视为 b
的当前值。expected = false
。循环不断循环; while(!false && !false);
expected = false
)直到出现上述任一情况。循环因为第二个条件只能在
expected = false
的时候一直循环下去,这也避免了做一个CAS(true,true)
而另一个线程持有锁时“成功”的问题。您不能将此循环用于一个普通的 lock()
函数,该函数一直旋转直到获得锁定(没有 !expected
检查);您必须在 expected = false
循环中重置 do{}while()
。
b = true;
的无条件存储不会告诉我们有关旧状态的信息。 我们可以使用tmp = b.exchange(true)
.
.exchange()
还需要 asm 循环用于 LL/SC 机器(CAS_weak
与 CAS_strong
不同的相同机器),并且它是无条件存储。 .fetch_add
和所有其他 RMW 原子也是如此。
compare_exchange_weak
存在的原因是CAS本身经常用于重试循环,例如合成任意原子 RMW 操作,如原子右移。如果您 are 打算在 CAS 失败时重试循环,那么它是否是虚假的并不重要,因此使用 CAS_weak 可以让编译器生成更简单的代码。
但是如果您只想进行一次 CAS 尝试,例如在某些算法中,例如
try_lock()
或各种其他情况,您应该使用 compare_exchange_strong
而不是编写自己的循环。