原子变量和原子操作

一、什么是原子操作

通常某一个变量的操作对应的CPU指令是大于一个的,在多线程环境下,为了确保对共享变量的操作在执行时不会被干扰,从而避免竞态条件和死锁等问题,使用原子变量。

原子变量可以看作是一种特殊的类型,它具有类似于普通变量的操作,但是这些操作都是原子操作,即要么全部完成,要么全部未完成。C++标准库提供了丰富的原子类型,包括整型、指针、布尔值等。通过std::atomic可以定义一个原子变量。

std::atomic<T>
is_lock_free: 是否支持无所操作
store(T desired, std::memory_order order): 写操作,用于将指定的值存储到原子变量中 
load(std::memory_order order): 读操作,用于获取原子变量的当前值
exchange(std::atomic<T> *obj, T desired): 访问和修改原子变量的值,将原子变量的值替换为 desired,并返回原来的值
compare_exchange_weak(T& expected, T val, memory_order success, ,memory_order failure): 比较一个值和期望值是否相等,如果不相等则将该值替换为一新值并返回 false,否则不做任何操作并返回 true.

二、原子性

1. 单处理器单核的情况

需要保证操作指令序列不被打断,实现机制:
屏蔽中断、底层自旋锁

2. 多核或者多处理器

除了不被打断,还需要避免其它核心操作相关的内存空间,实现方法:
lock 指令阻止其它核心对相关内存空间的访问

3. MESI 一致性协议

MESI 协议是一个基于失效的缓存一致性协议,支持 write-back 写回缓存的常用协议。
总线嗅探策略:将读写请求通过总线广播给所有核心,核心根据本地状态进行响应

Cache Line中的 flag 的四种状态:
Modified: 已修改,某个数据块在某核心中已修改但是没有同步到内存中
Exclusive: 独占,某个数据块只在某一个核心中,并且缓存和内存中的数据一致的
Shared: 共享,某个数据块在多个核心中加载,并且缓存和内存中数据一致
Involidated: 已失效,某个数据在核心中已失效,不是最新的数据

事件:

  • PrRd : 核心从缓存中读取数据请求
  • PrWr : 核心从缓存中写入数据请求
  • BusRd : 总线嗅探器收到来自其它核心的读出缓存数据请求
  • BusRdX : 总线嗅探器收到另一个核心写一个其不拥有的缓存块的请求
  • BusUpgr : 总线嗅探器收到另一个核心写一个其拥有的缓存块的请求
  • Flush : 总线嗅探器收到另一个核心把一个缓存块写回到主存的请求
  • FlushOpt :总线嗅探器收到一个缓存块被放置在总线以提供给另一核心的请求,和 Flush 类似但是为缓存到缓存的数据传输

状态机

三、内存序

为什么有内存序问题:

编译器优化重排
CPU指令优化重排

内存序规定了什么

规定了多个线程访问同一个内存地址时的语义:

  1. 某个线程对内存地址的更新何时能被其它线程看到
  2. 某个线程对内存地址访问附近可以做什么样的优化

内存模型

C/C++11标准中提供了6种memory order,来描述内存模型

enum memory_order {memory_order_relaxed,memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_rel,memory_order_seq_cst
};
memory_order_relaxed

relaxed表示一种最为宽松的内存操作约定,仅仅保证load()和store()是原子操作, 除此之外,不提供任何跨线程的同步,不干预编译器或CPU的优化

memory_order_release

在写入某原子对象时,当前线程的仍任何前面的读写操作都不允许重排到这个操作的后面,并且当前线程的所有内存写入都在对同一个原子对象进行获取的其它线程可见。通常与 memory_order_acquire 配对使用

memory_order_acquire

在读取某原子对象时,当前线程的任何后面的读写操作都不允许重排到这个操作的前面去,并且其它线程在对同一个原子对象释放之前的所有内存写入都在当前的线程中可见。通常与 memory_order_release 配合使用

memory_order_acq_rel

同时存在读写操作,前后读写操作都不允许重排。当前线程的所有内存写入都在对同一个原子对象进行获取的其它线程可见

memory_order_sec_cst

是所有原子操作的默认内存序,最严格的顺序一致性。会对所有使用此模型的原子操作建立一个全局顺序,保证了多个原子变量的操作,在所有线程里观察到的操作顺序相同,当然它是最慢的同步模型

四、使用原子操作实现无锁队列

使用场景

高性能、低延时场景中如信号处理程序,硬实时系统

MPSC队列具体实现

#include <atomic>
#include <utility>template<typename T>
class MPSCQueue{
private:struct Node{Node() = default;explicit Node(T* data) : Data(data){Next.store(nullptr, std::memory_order_relaxed);}T* Data;std::atomic<Node*> Next;};std::atomic<Node*> _head;std::atomic<Node*> _tail;
public:MPSCQueue() : _head(new Node()), _tail(_head.load(std::memory_order_relaxed)){Node* front = _head.load(std::memory_order_relaxed);front->Next.store(nullptr, std::memory_order_relaxed);}void Enqueue(T* input){Node* node = new Node(input);Node* prevHead = _head.exchange(node, std::memory_order_acq_rel);prevHead->Next.store(node, std::memory_order_release);}bool Dequeue(T*& result){Node* tail = _tail.load(std::memory_order_relaxed);Node* next = tail->Next.load(std::memory_order_acquire);if (!next)return false;result = next->Data;_tail.store(next, std::memory_order_release);delete tail;return true;}~MPSCQueueNonIntrusive(){T* output;while (Dequeue(output))delete output;Node* front = _head.load(std::memory_order_relaxed);delete front;}
};

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

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

相关文章

C++ 动态规划 计数类DP 整数划分

一个正整数 n 可以表示成若干个正整数之和&#xff0c;形如&#xff1a;nn1n2…nk &#xff0c;其中 n1≥n2≥…≥nk,k≥1 。 我们将这样的一种表示称为正整数 n 的一种划分。 现在给定一个正整数 n &#xff0c;请你求出 n 共有多少种不同的划分方法。 输入格式 共一行&…

Linux入门(1)Linux介绍

目录 1. 认识 Linux, 了解 Linux 的相关背景 1.发展史 2. 学会如何使用云服务器 3. 掌握使用远程终端工具 xshell 登陆 Linux 服务器 1. 认识 Linux, 了解 Linux 的相关背景 1.发展史 学习Linux系统编程&#xff0c;你可能要问Linux从哪里来&#xff1f;它是怎么发展的&am…

小游戏和GUI编程(7) | SimpleNN 界面源码解析

小游戏和GUI编程(7) | SimpleNN 界面源码解析 0. 简介 SimpleNN 是 AdamYuan 在高中一年级时用 1 天时间写出来的简易 CNN, 使用 SFML 做 UI, 用于交互式输入手写数字&#xff0c;这个数字被训练好的 CNN 网络执行推理得到识别结果, 它的运行效果如下&#xff1a; 这一篇我们…

【项目】高并发内存池

高并发内存池 【项目】高并发内存池项目介绍这个项目做的是什么&#xff1f; 内存池相关知识池化技术内存池malloc 定长内存池的实现高并发内存池整体框架设计ThreadCache对齐规则封装FreeList类封装thread cache类TLS无锁访问 CenctralCache整体设计页号规定span结构SpanList结…

2.8:Maefile、计算单词个数、判断文件类型、单词逆置

1.有main.c&#xff0c;test.c&#xff0c;test1.c&#xff0c;创建Makefile 程序代码&#xff1a; Makefile: 1 CCgcc2 EXEhello3 OBJS$(patsubst %.c,%.o,$(wildcard *.c))4 CFLAGS-c -o5 all:$(EXE)6 7 #hello依赖test.o main.o8 $(EXE):$(OBJS)9 $(CC) $^ -o $10 …

浅析Linux追踪技术之ftrace:Tracepoint

文章目录 概述Tracepoint使用定义Tracepoint添加Tracepoint调用 Tracepoint数据结构TRACE_EVENT实现DECLARE_TRACE__DECLARE_TRACE trace_xxx函数相关参考 概述 Tracepoint&#xff08;跟踪点&#xff09;是添加到代码流程中的调用点&#xff0c;并且允许开发者注册自定义的回…

TCP高频知识点

本篇文章主要讲述一下在面试过程中TCP的高频知识点 1.TCP三次握手流程图: 客户端发送一个SYN&#xff08;同步&#xff09;报文段给服务器&#xff0c;选择一个初始序列号&#xff0c;并设置SYN标志位为1。服务器接收到客户端的SYN报文段后&#xff0c;回复一个ACK&#xff08…

webgis后端安卓系统部署攻略,超详细Termux攻略

目录 前言 一、将后端项目编译ARM64 二、安卓手机安装termux 1.更换为国内源 2.安装ssh远程访问 3.安装文件远程访问 三、安装postgis数据库 1.安装数据库 2.数据库配置 3.数据导入 四、后端项目部署 五、自启动设置 总结 前言 因为之前一直做的H5APP开发&#xf…

如何利用SpringSecurity进行认证与授权

一、SpringSecurity简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#x…

【排序】归并排序

归并排序 动图演示&#xff1a; 基本思想&#xff1a;分治思想 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子…

深入理解C语言(4):文件操作详解

文章主题&#xff1a;文件操作详解&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3c4;&#x…

Linux中alarm/setitimer函数(信号函数)

alarm函数 函数原型&#xff1a; unsigned int alarm(unsigned int seconds); 函数描述&#xff1a;设置定时器&#xff08;闹钟&#xff09;。在指定seconds后&#xff0c;内核会给当前进程发送 14&#xff09;SIGALRM信号。进程收到该信号&#xff0c;默认动作终止。每个进程…