文章目录

    • 一、什么是原子操作?
    • 二、为什么需要原子操作?
    • 三、C++11中的<atomic>头文件
    • 四、基本使用
      • 1. 声明原子变量
      • 2. 基本原子操作
    • 五、内存顺序(Memory Order)
      • 示例:使用内存顺序实现自旋锁
    • 六、原子类型模板
    • 七、实际应用示例
      • 1. 线程安全的计数器
      • 2. 双重检查锁定(Double-Checked Locking)
    • 八、性能考虑
    • 九、常见陷阱
    • 十、总结

一、什么是原子操作?

原子操作(Atomic Operations)是指不可被中断的一个或一系列操作。在多线程编程中,原子操作就像是"不可分割的最小单位",要么完全执行,要么完全不执行,不会出现执行到一半被其他线程打断的情况。

二、为什么需要原子操作?

考虑以下场景:

int counter = 0;// 线程1
void increment() {counter++;
}// 线程2
void decrement() {counter--;
}

在多线程环境下,counter++counter-- 这样的操作实际上包含多个步骤(读取、修改、写入),可能导致竞态条件(Race Condition)。原子操作可以确保这些操作不可分割地完成。

三、C++11中的头文件

C++11引入了<atomic>头文件,提供了一系列原子类型和操作。主要包含:

  1. 基本原子类型(atomic_int, atomic_bool等)
  2. 类模板std::atomic<T>
  3. 原子操作函数
  4. 内存顺序控制

四、基本使用

1. 声明原子变量

#include <atomic>
#include <iostream>int main() {std::atomic<int> counter(0);  // 初始化为0counter.store(10);  // 原子存储int value = counter.load();  // 原子读取std::cout << "Counter: " << value << std::endl;return 0;
}

2. 基本原子操作

std::atomic<int> num(0);// 原子加法
num.fetch_add(5);  // 相当于 num += 5// 原子减法
num.fetch_sub(3);  // 相当于 num -= 3// 原子交换
int old = num.exchange(10);  // 将num设为10,返回旧值// 比较交换(CAS操作)
int expected = 10;
bool success = num.compare_exchange_strong(expected, 15);
// 如果num == expected,则设为15,返回true
// 否则将expected设为num的实际值,返回false

五、内存顺序(Memory Order)

内存顺序指定了原子操作周围的内存访问如何排序,是原子操作的高级特性。C++11定义了6种内存顺序:

  1. memory_order_relaxed:最宽松的顺序,只保证原子性
  2. memory_order_consume:依赖于此原子变量的后续操作不能重排序到它前面
  3. memory_order_acquire:后续操作不能重排序到它前面
  4. memory_order_release:前面操作不能重排序到它后面
  5. memory_order_acq_rel:acquire + release
  6. memory_order_seq_cst:最严格的顺序(默认)

示例:使用内存顺序实现自旋锁

#include <atomic>
#include <thread>class SpinLock {std::atomic_flag flag = ATOMIC_FLAG_INIT;public:void lock() {while (flag.test_and_set(std::memory_order_acquire)) {// 自旋等待}}void unlock() {flag.clear(std::memory_order_release);}
};

六、原子类型模板

std::atomic可以用于任何可平凡复制的类型:

struct Point {int x, y;
};std::atomic<Point> atomic_point;void updatePoint() {Point new_point{10, 20};atomic_point.store(new_point, std::memory_order_release);
}void readPoint() {Point current = atomic_point.load(std::memory_order_acquire);std::cout << "Point: (" << current.x << ", " << current.y << ")\n";
}

七、实际应用示例

1. 线程安全的计数器

#include <atomic>
#include <thread>
#include <vector>
#include <iostream>std::atomic<int> counter(0);
const int kIncrementsPerThread = 100000;
const int kThreadCount = 10;void increment() {for (int i = 0; i < kIncrementsPerThread; ++i) {counter.fetch_add(1, std::memory_order_relaxed);}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < kThreadCount; ++i) {threads.emplace_back(increment);}for (auto& t : threads) {t.join();}std::cout << "Final counter value: " << counter << std::endl;return 0;
}

2. 双重检查锁定(Double-Checked Locking)

class Singleton {
private:static std::atomic<Singleton*> instance;static std::mutex mutex;Singleton() {}public:static Singleton* getInstance() {Singleton* tmp = instance.load(std::memory_order_acquire);if (tmp == nullptr) {std::lock_guard<std::mutex> lock(mutex);tmp = instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton();instance.store(tmp, std::memory_order_release);}}return tmp;}
};std::atomic<Singleton*> Singleton::instance(nullptr);
std::mutex Singleton::mutex;

八、性能考虑

  1. 原子操作比普通操作慢,但比互斥锁快
  2. 使用memory_order_relaxed可以获得更好性能(当不需要严格顺序时)
  3. 避免过度使用原子变量,考虑无锁数据结构设计

九、常见陷阱

  1. 错误地认为原子操作可以替代锁:原子操作只保证单个操作的原子性,复杂操作仍需锁
  2. 忽略内存顺序:错误的内存顺序可能导致难以发现的bug
  3. ABA问题:在使用CAS操作时需要注意

十、总结

C++11的原子操作提供了:

  1. 线程安全的基本操作
  2. 多种内存顺序控制
  3. 比锁更高效的并发控制方式
  4. 构建无锁数据结构的基础

掌握原子操作是成为高级C++开发者的重要一步。建议从简单场景开始实践,逐步理解更复杂的内存顺序概念。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/86162.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86162.shtml
英文地址,请注明出处:http://en.pswp.cn/web/86162.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析Flink Local模式启动流程源码:揭开作业初始化的神秘面纱

在Flink的数据处理体系中&#xff0c;Local模式凭借无需依赖分布式集群资源的特性&#xff0c;成为开发测试阶段快速验证作业逻辑的利器。其启动流程的源码里&#xff0c;藏着从作业提交到任务执行的完整脉络。接下来&#xff0c;我们将深入关键代码段&#xff0c;逐行剖析Flin…

二刷 苍穹外卖 day06

HttpClient 用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包 作用&#xff1a; 发送HTTP请求 接受响应数据 应用场景&#xff1a; 当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时 其实&#xff0c;应用程序本身并未实现这些功能&#xff…

React第六十三节Router中BrowserRouter的用途及注意事项

前言 BrowserRouter 是 React Router 库的核心组件&#xff0c;用于实现单页面应用&#xff08;SPA&#xff09;的客户端路由。它利用 HTML5 History API 管理 URL&#xff0c;实现页面无刷新跳转。下面详细解释其用途、使用方法和代码示例&#xff1a; 一、BrowserRouter 核…

《Self-Adapting Language Models》(SEAL)代码阅读笔记

代码&#xff1a;https://github.com/Continual-Intelligence 脚本命令用法&#xff1a;knowledge-incorporation/README.md 生成self-edit数据 脚本&#xff1a;sbatch knowledge-incorporation/scripts/make_squad_data.sh vllm serve启动Qwen2.5-7B模型的服务。 执行self-e…

GelSight Mini视触觉传感器开发资源升级:触觉3D点云+ROS2助力机器人科研与医疗等应用

近日&#xff0c;GelSight宣布对其GelSight Mini视触觉传感器的GitHub支持页面进行重大更新&#xff0c;围绕3D点云重建、ROS2 集成及开发者支持体系推出三大核心升级&#xff0c;助力机器人触觉感知、工业检测及科研场景落地。 GelSight Mini视触觉传感器重磅发布&#xff01;…

6、做中学 | 三年级下期 Golang值类型相互转换

本次为操作文章&#xff0c;大部分都在讨论类型之间如何转换&#xff0c;使用的是内置方法进行调用执行&#xff0c;详细使用请移步至&#xff1a; go的API使用文档地址 https://studygolang.com/pkgdoc 一、数值类型相互转换 go中数值转换需要显示转换&#xff0c;不能隐式自…

019 高校心理教育辅导系统技术解析:构建心理健康守护平台

高校心理教育辅导系统技术解析&#xff1a;构建心理健康守护平台 在关注大学生心理健康成为教育重点的当下&#xff0c;高校心理教育辅导系统借助数字化技术整合多种功能模块&#xff0c;面向管理员、学生、教师三类角色&#xff0c;实现心理教育辅导工作的高效化与精准化。本…

【ArcGIS】土地资源单项评价

【ArcGIS】土地资源单项评价 一、土地资源单项评价1、评价思路 二、操作步骤1、处理环境设置2、地形坡度评价3、高程评价4、坡度高程叠加评价5、地形起伏度6、土地资源综合评价 一、土地资源单项评价 1、评价思路 &#xff08;1&#xff09;利用全域DEM计算地形坡度&#xff…

Prioritized Generative Replay

ICLR 2025 Oral code 具有样本效率的 online reinforcement learning (RL) 通常使用 replay buffer 存储经验&#xff0c;以便在更新价值函数时重复使用。然而&#xff0c;uniform replay 效率低下&#xff0c;因为某些类型的 transition 可能与学习更相关。 虽然对更有用的样本…

Linux -- 线程、锁

1、 Linux线程概念 1.1、什么是线程 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;更准确的定义是&#xff1a;线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行在Linux系统中…

海外服务器的定义和作用都有哪些?

海外服务器可以说是一个统称&#xff0c;其中包含了全球各地除了中国大陆以外其他国家的服务器&#xff0c;在如今的数字化时代中&#xff0c;海外服务器的应用已经成为跨国企业业务拓展、科研与学术交流等多个领域中不可或缺的一部分&#xff0c;能够为各个行业提供更加稳定且…

数据结构之优先级队列

系列文章目录 数据结构之ArrayList_arraylist o(1) o(n)-CSDN博客 数据结构之LinkedList-CSDN博客 数据结构之栈-CSDN博客 数据结构之队列-CSDN博客 数据结构之二叉树-CSDN博客 目录 系列文章目录 前言 一、优先级队列和堆 二、堆的模拟实现 1. 堆的创建 2. 计算建堆…

【版本控制教程】如何使用Unreal Engine 5 + UE源代码控制(Perforce P4)

本文来源perforce.com&#xff0c;由Perforce中国授权合作伙伴——龙智翻译整理&#xff0c;旨在为国内用户提供一份实用、易懂的Unreal Engine 5Perforce P4的中文使用指南。希望能为UE开发者、设计师和美术小伙伴们的版本控制实践提供有力支持~ Unreal Engine 5 是一款尖端的…

opensingleComDialog方法解析优化

下面是对 opensingleComDialog 方法的详细解析&#xff0c;并给出优化建议和优化后的代码。 方法解析 作用 opensingleComDialog(index) 方法用于在输入框失去焦点时&#xff08;blur 事件&#xff09;自动根据输入内容进行唯一性查询&#xff0c;如果查到唯一结果则自动填充…

css 实现1个像素在不同分辨率屏幕上画网格线

实现网格线绘制&#xff0c;要考虑画布style尺寸和画布像素大小的缩放关系 单像素绘制主要出现的问题是会模糊&#xff0c;从像素角度看就是出现绘制两个像素&#xff0c;实际就是要做偏移 核心就是&#xff1a;按物理像素绘制&#xff0c;首先要对齐物理像素&#xff0c;计算…

深度图聚类DGC—Paper Notes

目录 Unsupervised Deep Embedding for Clustering Analysis (DEC 2016)Attributed Graph Clustering: A Deep Attentional Embedding Approach (DAEGC 2019)Structural Deep Clustering Network (SDCN 2020)Contrastive Multi-View Representation Learning on Graphs (MVG…

获取YARN application 应用列表的几种方法

目录 1. 使用YARN命令行工具 2. 通过REST API获取 YARN 提供了获取YARN集群上运行的应用列表,以下是几种常见方法: 1. 使用YARN命令行工具 最直接的方式是使用YARN提供的命令行工具: yarn application -list 上述命令会显示所有正在运行的应用。 如果要查看所有应用(…

前端如何下载 ‘Content-Type‘: ‘application/octet-stream‘ 的文件

前言 在前端开发中&#xff0c;经常会遇到需要从后端接口下载文件的需求。当后端返回的响应头中 Content-Type 为 application/octet-stream 时&#xff0c;表示这是一个二进制流文件&#xff0c;浏览器无法直接展示&#xff0c;需要前端处理后下载到本地。本文将详细介绍前端…

咨询顾问进阶——顾问公司战略咨询分析模板【附全文阅读】

该战略咨询分析模板围绕企业战略分析展开&#xff0c;先从总体思考战略分析的目的与方法&#xff0c;接着探讨企业及战略定义、战略地位等。外部环境分析通过 PEST、五种竞争力等模型&#xff0c;分析环境、行业、市场等情况以发现机会与威胁&#xff1b;内部环境分析从资源、核…

宝塔服务器调优工具 1.1(Opcache优化)

第一步&#xff1a;宝塔服务器调优工具 1.1&#xff08;按照下面的参数填写&#xff09; 第二步&#xff1a;路径/www/server/php/80/etc/php.ini 搜索jit jit1235 其中1235根据服务器情况修改 第三步&#xff1a;路径/www/server/php/80/etc/php-cli.ini 搜索 jit1235 其中…