std::atomic_store 和 std::atomic_exchange 不交换
std::atomic_store and std::atomic_exchange do not exchange
根据 en.cppreference.com,std::atomic_exchange
和std::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
)
);
但是,这不是严格意义上的原子。 a
和b
可能会在几分之一秒内指向相同的地址。
就我而言,只要它们始终指向有效的、定义明确的对象,这种行为是可以接受的。
相关文章:
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 在 lambda 表达式中使用 std::atomic
- C++std::atomic在程序员级别保证了什么
- 如果在 2 个线程中使用,是否值得将size_t声明为 std::atomic?
- 在 C++20 之前和之后初始化 std::atomic
- std::atomic 和 std::mutex 的相对性能
- 简单使用 std::atomic 在两个线程之间共享数据
- Port pthread_cond_broadcast to std::atomic
- std::atomic中的任何内容都是免费等待的
- 为什么 std::atomic 构造函数在 C++14 和 C++17 中的行为不同
- std::atomic是如何实现的
- 使用 std::atomic 标志和 std::condition_variable 在工作线程上等待
- 为什么std::atomic的默认构造函数不默认初始化底层存储值
- 读取互斥对象范围之外的volatile变量,而不是std::atomic
- 为什么std::atomic中的所有成员函数都同时出现在有volatile和没有volatile的情况下
- 访问共享内存而不使用易失性、std::atomic、信号量、互斥锁和自旋锁
- 两个不同的进程,在同一地址上有 2 个 std::atomic 变量?
- 原子读取,然后使用 std::atomic 写入
- std::atomic::fetch_add是x86-64上的串行化操作
- 实际上,C++11 中 std::atomic 的内存占用量是多少?