C++20的std::atomic_ref可在不修改原变量定义前提下对其执行原子操作,需满足对齐、生命周期、类型匹配等约束,支持普通变量、数组元素、结构体成员的零开销原子访问。

如果您希望在不修改原始变量定义的前提下,对普通变量执行原子操作,则可以使用C++20引入的std::atomic_ref。以下是实现该目标的具体方法:
一、基本用法:构造atomic_ref并执行原子读-改-写
std::atomic_ref通过引用绑定到已存在的对象,要求该对象的地址对齐满足原子操作要求,且生命周期必须长于atomic_ref实例。绑定后可调用load、store、fetch_add等成员函数进行无锁操作。
1、声明一个普通int变量,确保其内存对齐满足atomic_ref要求(例如使用alignas(alignof(int)))。
2、使用该变量的引用构造std::atomic_ref
3、调用fetch_add(1)对变量执行原子自增,并获取原值。
4、调用load()读取当前值,验证修改结果。
二、处理数组元素:对数组中单个元素启用原子访问
当需要对动态分配或栈上数组中的特定元素实施原子操作,而又无法将整个数组声明为std::atomic类型时,atomic_ref提供了一种零开销的适配方式。它允许对任意满足对齐和生命周期约束的元素建立临时原子视图。
1、定义一个int类型数组,例如int arr[10];。
2、确认arr[5]的地址满足对齐要求(通常int默认对齐即可满足)。
3、构造std::atomic_ref
4、调用ref.exchange(42)将该位置原子地更新为42并返回旧值。
三、多线程共享结构体字段:对结构体内嵌普通成员进行原子操作
对于已定义的结构体,若其中某个成员未声明为atomic类型,但需在并发场景下安全修改,可通过atomic_ref绕过类型定义限制,直接对其内存位置施加原子语义。
1、定义结构体struct Data { int counter; double value; };,并创建实例Data d;。
2、检查&d.counter是否满足alignof(std::atomic_ref
3、构造std::atomic_ref
4、在多个线程中分别调用counter_ref.fetch_add(1, std::memory_order_relaxed)实现无竞争计数。
四、避免数据竞争:确保对象生命周期与对齐约束
使用atomic_ref的前提是所绑定对象的生存期必须覆盖所有atomic_ref的使用时段,且其存储必须按atomic_ref所要求的对齐方式布局;否则行为未定义。编译器不会自动提升对齐,需显式控制。
1、对局部变量使用atomic_ref时,不得将其传入异步任务或脱离作用域的lambda中。
2、对堆分配对象,使用new分配时应配合std::aligned_alloc或alignas指定对齐,例如alignas(std::atomic_ref
3、调用std::atomic_ref
五、类型匹配与cv限定符处理:正确传递引用类型
std::atomic_ref的模板参数T必须与所绑定对象的类型精确匹配,包括const/volatile限定符;绑定非常量对象时不可传入const T&,否则编译失败。这保证了原子操作权限与原始访问权限一致。
1、声明volatile int vval = 0;,则必须使用std::atomic_ref
2、声明const int cval = 5;,则只能构造std::atomic_ref
3、对非const普通变量int x = 10;,禁止使用std::atomic_ref
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











