【问题标题】:Is there a way to have a version of std::atomic's compare_exchange_strong method that exchanges on inequality?有没有办法让 std::atomic 的 compare_exchange_strong 方法在不等式上进行交换?
【发布时间】:2022-01-23 04:05:08
【问题描述】:

我有一个原子类型,我需要将它与一个值进行原子比较,如果两个值不相等,则交换原子的值。

换句话说,compare_exchange_strong 本质上是原子地执行此操作:

if (atomic_value == expected)
    atomic_value = desired;

...我正在寻找一种方法来做到这一点:

if (atomic_value != expected)
    atomic_value = desired;

(是的,我知道compare_exchange_strong 使用按位相等进行比较,而不是== 运算符。而且我知道当比较失败时会分配expected 的值。这仅用于说明目的。在我的用例中无论比较结果如何,我都不需要原子的值。)

有什么方法可以做到这一点而不必依赖使用锁而不是std::atomic

【问题讨论】:

  • 看看是否有任何建议的答案在 LL/SC 架构上得到最佳编译会很有趣,在机器级别,这实际上只是一个逆向测试的问题。

标签: c++ atomic stdatomic compare-and-swap


【解决方案1】:

只需使用通常用于比较和交换的循环,但不是循环直到(新)预期值匹配,而是循环或者直到它匹配(并且存储发生)或者它等于您的 != expected 条件中的值,因为在这种情况下您不需要做任何事情。 (显然不要将初始值设为“除非”值,以免第一次“成功”。)

【讨论】:

  • 恐怕我没跟上。你能澄清你的答案吗?
  • @BriBri 与自己交换一个值是一个 nop,所以只需执行一个 variable.exchange(expected_value) 并且如果返回值不等于 expected_value,然后做你的事。
  • @RaymondChen:很确定你不能在这里无条件地使用.exchange(),否则你会让其他线程看到一个可能不应该存在的值。这只有在expected == desired 时才是安全的,即一个普通的存储来设置值,无论它是否已经是那个值。或者,如果特定用例允许在存储desired 之前临时设置variable = expected,即其他线程也在尝试这样做,而不是对共享变量进行其他一些操作。
  • @PeterCordes 好的,我误解了这个问题。我认为有条件分配的值是expected。 (即if (variable != expected) { variable = expected; }
  • @RaymondChen:是的,我也花了一秒钟才发现这一点。该模式的通常原因是避免编写器(这会使其他内核的缓存行无效),所以我认为您通常也不希望将其变成.exchange。虽然在只读检查后可能是.exchange,但如果您需要验证您确实赢得了更改变量的竞赛。
【解决方案2】:
auto observed = atomic_value.load();
for (;;)
{
    if (observed == expected){
       break; // no exchange
    }
    if (atomic_value.compare_exchange_weak(observed, desired)) { 
       break; // successfully exchanged observed with desired
    }
}

当然,它在 HW 具有 LL/SC 的架构上不是最理想的,因为 C++ 没有公开它。 LL/SC 可以有任意条件。

【讨论】:

  • 由于比较和交换不是同一个原子操作的一部分,这不是有竞争条件吗?另一个线程可以在对load() 的调用和比较之间或在比较和交换之间更改atomic_value
  • @BriBri:如果另一个线程更改了值,则compare_exchange_weak 返回false,因为atomic_value 不再等于observed。它将新值加载到observed,我们继续循环以将新值与expected 进行比较。这一切似乎都是正确的,我没有看到任何比赛。
【解决方案3】:

你可以使用这样的东西:

#include <atomic>
#include <random>

std::atomic<int> atomVal;

int store_if_not_equal(int desired)
{
    while (true)    // or maxloop....
    {
        int expected = desired;

        if (atomVal.compare_exchange_strong(expected, desired))
        {
            // values matched - do nothing
            return 0;
        }
        else
        {
            //expected now contains the "current value"
            // another thread could have sneaked in and changed it,
            // so only replace it if it still matches
            if (atomVal.compare_exchange_strong(expected, desired))
            {
                // success
                return 1;
            }
        }

    // if we arrive here, retry
    }
}

int main()
{
    atomVal.store(rand());
    return store_if_not_equal(2);
}

演示:https://godbolt.org/z/qWTP7canf

【讨论】:

  • 如果比较通过,第一次调用compare_exchange_strong 是否会将atomVal 设置为desired?如果atomVal 的值等于expected,则不应修改atomVal
  • 它被“修改”为相同的值。
  • 这可能适用于某些情况,但在我的用例中expecteddesired 不一定是相同的值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多