替换
C++并发编程(二十)
前言
原子操作除了模板特化后的成员函数,还可以使用标准库中的普通函数。
这种函数前缀为 std::atomic_,包括std::atomic_load(),std::atomic_store(),std::atomic_exchange(),std::atomic_compare_exchange_weak(),std::atomic_compare_exchange_strong() 等。
以上是不可以设置内存次序参数的函数,如需设置内存次序,则需要在函数名后加后缀 _explicit。
另外,C++ 还允许 std::shared_ptr<> 对象作为非成员原子操作函数的参数,使得其支持基本的原子操作(载入,存储,交换,比较替换)。
一、原子操作的非成员函数
以下示例,展示了原子操作的非成员函数使用,和原子类成员函数区别不大,只是有两个版本,一个不可设置内存次序,一个可设内存次序。
可通过 debug 观察每一步的变化。
另外,虽然智能共享指针 std::shared_ptr<> 可以用于原子操作,但其不是无锁结构。
#include <atomic>
#include <iostream>
#include <memory>
struct atomicObj
{
atomicObj() = default;
atomicObj(int I, float F)
: i(I)
, f(F)
{}
private:
int i = 0;
float f = 0;
};
auto main() -> int
{
std::atomic<atomicObj> obj;
//原子操作函数的参数为原子类对象指针
std::cout << std::atomic_is_lock_free(&obj) << std::endl;
//无内存次序版本
std::atomic_store(&obj, atomicObj{1, 1.0F});
//有内存次序版本
std::atomic_store_explicit(&obj, atomicObj{2, 2.0F},
std::memory_order_release);
atomicObj test = std::atomic_load(&obj);
test = std::atomic_load_explicit(&obj, std::memory_order_acquire);
atomicObj test2 = std::atomic_exchange(&obj, atomicObj{3, 3.0F});
test = std::atomic_exchange_explicit(&obj, atomicObj{4, 4.0F},
std::memory_order_seq_cst);
//比较替换的期望值 test 也是指针形式
while (std::atomic_compare_exchange_weak(&obj, &test, atomicObj{5, 5.0F}))
{
}
//比较替换的内存次序为两个,不可省略
std::atomic_compare_exchange_weak_explicit(&obj, &test, atomicObj{6, 6.0F},
std::memory_order_seq_cst,
std::memory_order_relaxed);
std::atomic_compare_exchange_strong(&obj, &test, atomicObj{6, 6.0F});
std::atomic_compare_exchange_strong_explicit(
&obj, &test, atomicObj{6, 6.0F}, std::memory_order_seq_cst,
std::memory_order_relaxed);
std::shared_ptr<atomicObj> sharedPtr = std::make_shared<atomicObj>(7, 7.0F);
//智能共享指针 shared_ptr<> 的指针也可以作为原子操作函数的参数
//但其并非无锁结构
std::cout << std::atomic_is_lock_free(&sharedPtr) << std::endl;
std::shared_ptr<atomicObj> local = std::atomic_load(&sharedPtr);
local = std::atomic_load_explicit(&sharedPtr, std::memory_order_seq_cst);
local =
std::atomic_exchange(&sharedPtr, std::make_shared<atomicObj>(8, 8.0));
local = std::atomic_exchange_explicit(&sharedPtr,
std::make_shared<atomicObj>(8, 8.0),
std::memory_order_seq_cst);
std::atomic_store(&sharedPtr, local);
std::atomic_store_explicit(&sharedPtr, local, std::memory_order_seq_cst);
while (std::atomic_compare_exchange_weak(
&sharedPtr, &local, std::make_shared<atomicObj>(9, 9.0F)))
{
}
while (std::atomic_compare_exchange_weak_explicit(
&sharedPtr, &local, std::make_shared<atomicObj>(9, 9.0F),
std::memory_order_seq_cst, std::memory_order_relaxed))
{
}
std::atomic_compare_exchange_strong(&sharedPtr, &local,
std::make_shared<atomicObj>(10, 10.0F));
std::atomic_compare_exchange_strong_explicit(
&sharedPtr, &local, std::make_shared<atomicObj>(10, 10.0F),
std::memory_order_seq_cst, std::memory_order_relaxed);
return 0;
}
总结
虽然标准库准备了非成员函数的原子操作函数,但我想,如果是特化的原子类,还是用成员函数比较方便,且不容易出错,这种非成员函数,可能更主要的应用是智能共享指针的原子操作。