C++20中的std::atomic_ref
一、std::atomic_ref
我们在学习C++11后的原子操作时,都需要提前定义好std::atomic变量,然后才可以在后续的应用程序中进行使用。原子操作的优势在很多场合下优势非常明显,所以这也使得很多开发者越来习惯使用原子变量。
但是,在实际的开发场景中,有一些情况变量已经被定义或者说被封装,再想更改使用相关的变量非常麻烦或者说不可能,有没有一些方法可以将其转成原子操作呢?这就是在C++20中提供的std::atomic_ref。它可以把变量转成原子引用(即对其引用的对象进行原子操作,笔者自己为记忆方便定义为原子引用)。先看其定义:
template< class T >
struct atomic_ref;
其内部支持wait、notify_one和notify_all等线程间的同步机制。那么在多线程中应用更灵活方便。
二、应用和限制
std::atomic_ref特别适合那些与其它模块甚至第三方已定义完成的对象定义进行原子操作控制。这样,在简单的应用场景下就可以不用再考虑多线程的并发问题,减轻了开发者的负担,增加了数据的安全性,提高了性能。其应用简单方便,不必引入过重的锁的控制,既实现了多线程数据间的安全操作又减少了锁的应用造成的较大的开销。
不过,有优势的地方就必然也有受限制的地方:
1、要防止多个地方操作被引用的数据(混合使用),比如直接使用数据和使用std::atomic_ref使用数据,可能会导致数据的混乱,这点可参看普通指针和智能指针混合使用。
2、如果使用const,需要注意:const对std::atomic_ref是浅层的,即可以通过其它地方对引用的数据进行修改,切记
3、使用std::atomic_ref时引用对象要求必须是平凡可复制的且不能是:“is_always_lock_free is false and std::is_volatile_v is true”。
4、和所有的引用一样,std::atomic_ref引用的对象生命周期要长于std::atomic_ref引用本身
5、被原子引用所引用的对象所需的对齐要求,至少为 alignof(T)
6、要选用合理的memory_order,不过在PC机上编程一般std::memory_order_relaxed几乎都可以满足
7、一定要清楚,std::atomic_ref并不是为了替代锁而产生的,所以在实际编程中要综合考虑如何应用同步机制
8、在高并发(并行)操作时,还是需要注意性能瓶颈的产生
应用简单,就更需要注意限制的场景,否则随意使用,极有可能反而达不到实际想要的结果。
三、例程
先看一个基础的例程:
#include <atomic>
#include <iostream>
#include <thread>
struct Data {
int value;
};
int main() {
Data data={0};
static int d = 9;
std::atomic_ref<int> atomicValue = std::atomic_ref(data.value);
std::atomic_ref<int> addValue = std::atomic_ref(d);
auto addThread1 = [&atomicValue]() {
for (int i = 0; i < 1000; ++i) {
atomicValue.fetch_add(1, std::memory_order_relaxed);
}
};
auto addThread2 = [&addValue]() {
for (int i = 0; i < 1000; ++i) {
addValue.fetch_add(1, std::memory_order_release);
}
};
std::thread t1(addThread1);
std::thread t2(addThread1);
std::thread t3(addThread2);
std::thread t4(addThread2);
t1.join();
t2.join();
t3.join();
t4.join();
std::cout << "Final value: " << data.value << std::endl; // 2000
std::cout << "Final value: " << d << std::endl;//2009
addValue.store(20,std::memory_order_relaxed);
std::cout<<"read d:"<<addValue.load(std::memory_order_relaxed)<<std::endl;
return 0;
}
多线程中的同步,和普通的锁同步一样,这里就不举例子了。可参看一下相关的wait等方法即可。
四、总结
看到std::atomic_ref,其实就想到了智能指针,二者应用的方式何其相似。从目的上来讲就是为了让开发变得简单,特别是在多线程状态下的开发,能简单一些是一些。另外就是为了兼容更多的原有的应用让其都能使用std::atomic的功能。
或者从另外一个角度看,std::atomic_ref只是一个过渡的功能,以后会出现一个更强大的类或接口,让原子操作更简单。让我们拭目以待!