std::atomic_store 和 std::atomic_exchange 不交换

std::atomic_store and std::atomic_exchange do not exchange

本文关键字:std atomic 交换 store exchange      更新时间:2023-10-16

根据 en.cppreference.com,std::atomic_exchangestd::atomic_store相当于线程安全的std::swap。但这不是我在 g++ 或 clang++ 中得到的行为。

问题住在大肠杆菌上。(见下文)

不过,它打印了这个:

std::atomic_store
a: 0x1ed2c30    0
b: 0x1ed2c50    1
a: 0x1ed2c50    1
b: 0x1ed2c50    1
std::atomic_exchange
a: 0x1ed2c50    0
b: 0x1ed2c30    1
a: 0x1ed2c30    1
b: 0x1ed2c30    1

这是为什么呢?我做错了什么吗?我是否看错了文档?

代码清单

#include <iostream>
#include <memory>
int main()
{
    {
        std::cout << "std::atomic_storenn";
        auto a = std::make_shared<int>(0);
        auto b = std::make_shared<int>(1);
        std::cout
        << "a: " << a.get() << 't' << *a << 'n'
        << "b: " << b.get() << 't' << *b << 'n' << std::endl;
        std::atomic_store(&a, b);
        std::cout
        << "a: " << a.get() << 't' << *a << 'n'
        << "b: " << b.get() << 't' << *b << 'n' << std::endl;
    }
    {
        std::cout << "std::atomic_exchangenn";
        auto a = std::make_shared<int>(0);
        auto b = std::make_shared<int>(1);
        std::cout
        << "a: " << a.get() << 't' << *a << 'n'
        << "b: " << b.get() << 't' << *b << 'n' << std::endl;
        std::atomic_exchange(&a, b);
        std::cout
        << "a: " << a.get() << 't' << *a << 'n'
        << "b: " << b.get() << 't' << *b << 'n' << std::endl;
    }
}

这种描述有些误导。

它说,例如,

template<class T>
void atomic_store( std::shared_ptr<T>* p,
                   std::shared_ptr<T> r );

"有效"确实p->swap(r).就目前而言,这是正确的(实际上也是标准所说的)。

但是,r 是按值传递的函数参数,因此在函数返回之前被销毁。它不会影响调用方中的任何内容。

std::atomic_exchange不交换 a 和 b,它将 a 设置为 b 并返回 a 的前一个值。

你可以做:

b = std::atomic_exchange(&a, b);

这将按您的预期工作(交换 a 和 b 的指针),但这不是线程安全的:如果多个线程同时访问对象b,这是未定义的行为。您不能做得更好,因为共享指针是一个复杂结构,通常包含两个数据成员(两个指针)。由于原子无锁交换需要硬件支持,因此这仅适用于基本类型(整数和指针)。

多年来,

我一直感到困惑,为什么"比较和交换"在其中swap。它不会交换任何东西。它设置检查预览的值,如果检查失败,则返回当前值。因此,atomic_exchange绝不等同于std::swap。

相反,它是一种检查和设置模式,所以我总是更喜欢 CAS 表示比较和设置。

看起来最接近我想要的行为是这样的:

    std::atomic_store(
        &b,
        std::atomic_exchange(
            &a,
            b
        )
    );

但是,这不是严格意义上的原子。 ab可能会在几分之一秒内指向相同的地址。

就我而言,只要它们始终指向有效的、定义明确的对象,这种行为是可以接受的。