没有真正理解std::atomic::compare_exchange_weak和compare_exchange_strong的逻辑

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

我已阅读https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange

以原子方式比较对象表示(直到 C++20)值 *this 与预期的表示 (C++20 起),并且如果 这些是按位相等的,将前者替换为所需的(执行 读-修改-写操作)。否则,加载实际存储的值 in *this 进入预期(执行加载操作)。

据我了解代码如下

bool expected=true;
extern atomic<bool> b = false; 
void foo ()
{
//
    while(!b.compare_exchange_weak(expected, false));
//
}

循环运行一次后(忽略虚假失败)它将失败,并将写入预期的

false
,因此在第二次迭代时compare_exchange_weak将返回成功,尽管
b
尚未更改为true。但这一切有什么意义呢?我虽然可以用它作为同步锁,等待其他线程改变
b
,但现在我想不出这个的用法。

cppreference 中的示例还表明,两次调用compare_exchange_strong 后将会成功。

#include <atomic>
#include <iostream>
 
std::atomic<int>  ai;
 
int  tst_val= 4;
int  new_val= 5;
bool exchanged= false;
 
void valsout()
{
    std::cout << "ai= " << ai
          << "  tst_val= " << tst_val
          << "  new_val= " << new_val
          << "  exchanged= " << std::boolalpha << exchanged
          << "\n";
}
 
int main()
{
    ai= 3;
    valsout();
 
    // tst_val != ai   ==>  tst_val is modified
    exchanged= ai.compare_exchange_strong( tst_val, new_val );
    valsout();
 
    // tst_val == ai   ==>  ai is modified
    exchanged= ai.compare_exchange_strong( tst_val, new_val );
    valsout();
}

结果:

ai= 3  tst_val= 4  new_val= 5  exchanged= false
ai= 3  tst_val= 3  new_val= 5  exchanged= false
ai= 5  tst_val= 3  new_val= 5  exchanged= true
c++ multithreading atomic stdatomic compare-and-swap
2个回答
6
投票

std::atomic::compare_exchange_weak
以线程感知的方式执行此英语任务:

因为变量保持

expected
,它现在应该保持
desired

作为一项微不足道的任务,想象一下您的

std::atomic<int> x
的值应该是平方。但其他线程可能正在修改它,因此您不能简单地读取该值,对其求平方并将新值写回。读完后数值可能会改变!

这是执行此任务的线程安全方法。保证您存储的值将被其平方替换。

int expected = x;
while( !x.compare_exchange_weak(expected, expected*expected) ) {}

此代码将自动用其平方替换

expected
除非值已更改。

如果值已更改,

expected
现在会使用新值进行更新,并且代码会再次尝试。


3
投票

我将举一个我使用它的例子,因为它非常简单。

我有atomic,它描述了某物的可用大小。 存在整数溢出的危险,所以我必须在减去值之前先进行检查。

我的生产代码不完全复制粘贴:

class LockFreeCuncuretSizeLimit {
public:
    explicit LockFreeCuncuretSizeLimit(size_t available) : mSize{available}
    {}

    bool tryAcuqire(size_t s) {
        size_t lastSize = mSize;
        size_t newSize;
        do
        {
            if (lastSize >= s)
            {
                newSize = lastSize - s;
            }
            else
            {
                return false;
            }

        }
        while (!mSize.compare_exchange_weak(lastSize, newSize));
        return true;
    }

    void release(size_t s) {
        mSize += s; // here I do not have worry about integer overflow
    }
private:
    std::atomic<size_t> mSize;
};

现在尝试在没有

compare_exchange_strong
并且没有竞争条件的情况下执行图像操作。

满足条件的情况发生了变化,但是当在原子上完成减法时,其他一些线程已经减去了一个值,因此当我进行实际减法时,可能会溢出整数。所以如果没有

compare_exchange_strong
,这是不可能完成的。

现在

compare_exchange_strong
compare_exchange_weak
之间的区别很难解释。甚至 Herb Sutter 在一些 cppcon 演讲中也放弃了解释,并提供了简单的规则:“如果需要循环,请使用
compare_exchange_weak
,否则使用
compare_exchange_strong
”。

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