关于使用原子布尔类型的 compare_exchange_swap 的问题

问题描述 投票:0回答:1

这里引用了本次 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) 的事情吗?

c++ c++17 c++14 stdatomic compare-and-swap
1个回答
1
投票

我认为这是一个

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);

三种情况是:

  • CAS 成功,循环退出而不检查
    expected
    的值。
    while(false && ...)
    .
    这是成功获得循环的所有权,我们可以进入临界区。
  • CAS 失败,
    expected = true
    。循环退出;
    while(!false && !true);

    这是非虚假故障:CAS 将
    false
    以外的值视为
    b
    的当前值。
  • CAS 失败,
    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
而不是编写自己的循环。

© www.soinside.com 2019 - 2024. All rights reserved.