当前位置: 首页 > news >正文

原子操作(C++)

原子操作

  • 一、原子操作与 C++ 内存模型概述
  • 二、std::atomic 基本用法(C++11–17)
    • 1. 头文件与模板
    • 2. Lock‐free 保证
    • 3. 常用操作接口
      • 1. store / load
      • 2. exchange
      • 3. compare_exchange_weak / compare_exchange_strong
      • 4. fetch_add / fetch_sub / fetch_and / fetch_or / fetch_xor
      • 5. atomic_thread_fence
      • 6. std::atomic_flag 的专用操作
      • 小结
  • 四、C++17 中的改进点
  • 五、C++20/23 的新特性
    • 1. std::atomic_ref(C++20)
    • 2. 原子等待/通知(C++20)
    • 3. 并发构件(C++20/23,<latch>, <barrier>, <semaphore>)
    • 4. 其他细节
  • 六、使用建议与注意事项

一、原子操作与 C++ 内存模型概述

  • 原子性 (Atomicity)
    原子操作在多线程并发下要么全部完成,要么全部不做,不会被线程调度中断。

  • 可见性 (Visibility)
    一个线程对内存的写入,如果不通过原子操作或合适的内存屏障,其他线程可能无法及时看到。

  • 有序性 (Ordering)
    在乱序执行的 CPU 与优化过的编译器下,指令执行与内存访问可能与源代码顺序不一致,需通过内存序(memory order)来控制。

C++ 自 C++11 起定义了正式的跨平台内存模型,彻底解决了 “数据竞争” 问题。所有而非原子操作的并发读/写同一内存区都属于未定义行为。

二、std::atomic 基本用法(C++11–17)

1. 头文件与模板

#include <atomic>// 常用特化
std::atomic<int>    a_int;
std::atomic<bool>   a_bool;
std::atomic<void*>  a_ptr;
  • 对内置整型、指针、bool 等都有专门的特化。

  • std::atomic 仅对“满足TriviallyCopyable”的类型有效。

2. Lock‐free 保证

  • a.is_lock_free():运行时查询该原子对象在当前平台是否无锁实现。

  • std::atomic::is_always_lock_free (constexpr,自 C++17):编译期常量,指示在所有平台上是否都无锁。

static_assert(std::atomic<int>::is_always_lock_free, "int should be lock-free");

3. 常用操作接口

操作原型说明
store(v, mo)void store(T v, memory_order mo = memory_order_seq_cst)原子写
load(mo)T load(memory_order mo = memory_order_seq_cst)原子读
exchange(v, mo)T exchange(T v, memory_order mo = memory_order_seq_cst)交换并返回旧值
compare_exchange_weak/strong(expected, desired, mo…)bool compare_exchange_weak(T& expected, T desired, memory_order…);CAS 操作,失败时 expected 更新为实际值
fetch_add/sub/and/or/xorT fetch_add(T v, memory_order mo = …)原子加减与位运算
atomic_thread_fence(mo)void atomic_thread_fence(memory_order mo)全局内存栅栏
注:所有接口可指定 memory_order,详见下节。

1. store / load

函数原型

void store(T desired,std::memory_order mo = std::memory_order_seq_cst) noexcept;
T    load(std::memory_order mo = std::memory_order_seq_cst) const noexcept;

语义

  • store:将原子对象的值写为 desired。

  • load:原子地读取当前值并返回。

默认内存序

  • seq_cst:全局顺序,最严格。
cpp
std::atomic<int> a{0};
// 写入
a.store(42);  // 等价于 a.store(42, memory_order_seq_cst);
// 读取
int v = a.load();  // v == 42

注意

  • memory_order_relaxed 下,只保证原子性,不保证可见性或顺序。

  • memory_order_release 仅对 store 有效;memory_order_acquire 仅对 load 有效。

2. exchange

函数原型

T exchange(T desired,std::memory_order mo = std::memory_order_seq_cst) noexcept;

语义

  • 将原子对象的值替换为 desired,返回替换前的旧值。

  • 原子地执行交换操作。

示例

std::atomic<int> a{5};
int old = a.exchange(10);  // old == 5, a == 10

用途

  • “取旧值并写新值” 场景,如实现简单的互斥标志。

3. compare_exchange_weak / compare_exchange_strong

函数原型

bool compare_exchange_weak(T& expected, T desired,std::memory_order success = std::memory_order_seq_cst,std::memory_order failure = std::memory_order_seq_cst) noexcept;bool compare_exchange_strong(T& expected, T desired,std::memory_order success = std::memory_order_seq_cst,std::memory_order failure = std::memory_order_seq_cst) noexcept;

语义

  • CAS(Compare‐And‐Swap):如果当前值等于 expected,则原子地写入 desired 并返回 true;否则将当前值载入 expected 并返回 false。

区别:

  • weak 可能 伪失败(spuriously fail)——常用于自旋循环,可提高性能。

  • strong 保证只有在值不匹配时才失败。

示例

std::atomic<int> a{0};
int expected = 0;
bool ok = a.compare_exchange_weak(expected, 1);
// 如果 a == 0,则写入 1 并 ok==true;
// 否则 expected 被赋为 a 的当前值,ok==false。

4. fetch_add / fetch_sub / fetch_and / fetch_or / fetch_xor

函数原型

T fetch_add(T arg,std::memory_order mo = std::memory_order_seq_cst) noexcept;
T fetch_sub(T arg,std::memory_order mo = std::memory_order_seq_cst) noexcept;
T fetch_and(T arg,std::memory_order mo = std::memory_order_seq_cst) noexcept;
T fetch_or (T arg,std::memory_order mo = std::memory_order_seq_cst) noexcept;
T fetch_xor(T arg,std::memory_order mo = std::memory_order_seq_cst) noexcept;

语义
对当前值执行指定的算术或按位运算,返回 原子操作前 的旧值。

示例

复制
编辑
std::atomic<int> counter{0};
// 累加
int prev = counter.fetch_add(3); // prev==0, counter==3
// 按位或
std::atomic<int> flags{0b0011};
int oldf = flags.fetch_or(0b1100); // oldf==0b0011, flags==0b1111

5. atomic_thread_fence

函数原型

void atomic_thread_fence(std::memory_order mo) noexcept;

语义

  • 在调用位置插入 全局内存栅栏。

  • 保证在栅栏前后的非原子访问遵循指定的排序语义。

示例

int data = 0;
std::atomic<bool> ready{false};// 写线程
data = 42;
atomic_thread_fence(std::memory_order_release);
ready.store(true, std::memory_order_relaxed);// 读线程
if (ready.load(std::memory_order_relaxed)) {atomic_thread_fence(std::memory_order_acquire);// 这里一定能看到 data==42
}

6. std::atomic_flag 的专用操作

函数原型

std::atomic_flag flag = ATOMIC_FLAG_INIT;
bool test_and_set(std::memory_order mo = std::memory_order_seq_cst) noexcept;
void clear(std::memory_order mo = std::memory_order_seq_cst) noexcept;

语义

  • test_and_set:原子地将标志置1,返回先前值。

  • clear:将标志置0。

cpp
std::atomic_flag lock = ATOMIC_FLAG_INIT;void acquire() {while (lock.test_and_set(std::memory_order_acquire)) {; // spin}
}
void release() {lock.clear(std::memory_order_release);
}

小结

  • 选择接口:简洁的读写用 load/store;需要“交换”用 exchange;需要条件更新用 CAS;计数与标志位操作用 fetch_*;全局栅栏用 atomic_thread_fence;轻量锁用 atomic_flag。

  • 内存序权衡:默认 seq_cst 最安全;对性能敏感时可以降级到 acquire/release 或 relaxed。

  • 性能注意:

    • CAS 自旋要配合弱版(weak)使用,并写成循环;

    • 避免在大对象上使用原子;

    • 对简单标志推荐使用 atomic_flag。

四、C++17 中的改进点

  • is_always_lock_free 成为 constexpr
    便于在编译期静态断言 lock‐free 保证。

  • std::atomic 的 constexpr 构造
    从 C++17 起,原子对象可在常量表达式中初始化:

constexpr std::atomic<int> a{0};
  • 内存模型与编译器优化的细微修正
    标准更精确地界定了 consume 的含义(尽管大部分实现依然把它当成 acquire 来处理)。

五、C++20/23 的新特性

1. std::atomic_ref(C++20)

对已有对象提供原子视图,无需把原类型改为 std::atomic:

int  counter = 0;
std::atomic_ref<int> a(counter);
a.fetch_add(1, std::memory_order_acq_rel);
// counter 也随之变化
  • 语义与 std::atomic 基本一致,但不占用额外内存。

2. 原子等待/通知(C++20)

  • wait/notify_one/notify_all:在整型或指针类型上直接阻塞与唤醒,替代条件变量的部分场景。
std::atomic<int> flag{0};// 等待线程
if (flag.load() != 1)flag.wait(0);  // 阻塞直到不等于0// 唤醒线程
flag.store(1);
flag.notify_one();

3. 并发构件(C++20/23,, , )

  • 虽不属于原子模板,但与原子操作密切相关,提供更高层的同步原语。

4. 其他细节

  • 在 C++23 中,std::atomic_flag::wait/notify_* 同步功能完善。

  • 针对 shared_ptr、unique_ptr 的原子支持仍在并行标准/TS 中演进。

六、使用建议与注意事项

  • 只在真正需要无锁同步时使用原子操作,不宜滥用;

  • 对复杂同步场景,优先考虑 mutex+condition_variable 或并发算法库;

  • 精确选择内存序:默认 seq_cst 最安全,对性能敏感时再切换到更松的语义;

  • 警惕 memory_order_consume 在主流编译器上的支持不足。

http://www.xdnf.cn/news/657613.html

相关文章:

  • 深度体验:海螺 AI,开启智能创作新时代
  • liunx、ubantu22.04安装neo4j数据库并设置开机自启
  • AI工程师跑路了-SpringAi来帮忙
  • 学习路之PHP--easyswoole安装入门
  • LINUX安装运行jeelowcode前端项目
  • SC89171的介绍和使用
  • 炫云云渲染,构筑虚实交融的3D数字新视界
  • AI的“软肋”:架构设计与业务分析的壁垒
  • OpenCV CUDA模块图像过滤------创建一个行方向的一维积分(Sum)滤波器函数createRowSumFilter()
  • 爬虫IP代理效率优化:策略解析与实战案例
  • Neo4j(三) - 使用Java操作Neo4j详解
  • 第12次05: 用户中心-用户基本信息
  • 如何用ChatGPT提升学术长文质量
  • Golang Gin框架基础与实践指南
  • 【学习笔记】GitLab 下载安装与配置
  • 算力服务器的应用场景都有哪些
  • 学习python day8
  • 超临界机组协调控制系统建模项目开发笔记
  • git 删除某个远程库的分支
  • 【Redis】第1节|Redis服务搭建
  • 【freertos-kernel】queue(创建)
  • 企业网络综合实训
  • Zephyr OS: periodic_adv_rsp代码架构和实现
  • GPT-4o 风格提示词案例大全(持续更新 ing...)
  • 小白成长之路-计算机网络(二)
  • 前后端分离项目之新增编辑功能
  • 4800H 低负载黑屏或者蓝屏
  • JS逆向【抖查查】逆向分析 | sign | secret签名验证
  • 亚马逊竞争指数下降20%?这些类目正成新蓝海
  • linux centos 服务器性能排查 vmstat、top等常用指令