C++ 标准库 std::atomic 及 std::memory_order

C++ 标准库提供了原子操作。(我已经懒得写序言了)

====================================

先来说原子操作的概念:

原子操作是多线程当中对资源进行保护的一种手段,主要作用是和互斥量(Mutex)一样,避免对资源的并发访问、修改。

互斥量的粒度衡量是作用域(哪怕作用域内只有一个变量),而原子的粒度衡量则是以一个变量或对象为单位。因此,原子相对于互斥量更加高效,但并非替代关系。

互斥量的主要作用是保护作用域内的资源,而原子的作用是保护一个变量或对象。

因此,当你需要保护的资源仅仅是某个变量或对象时,应首先考虑使用原子。

1,std::atomic

如果你并不明白 std::atomic (原子) 的作用,请看以下代码及执行结果:

执行结果:

以上代码分别定义了两个 int 变量,一个是普通的变量,一个是原子变量。两个变量分别用两个线程去递增1000000次。

理论上,两个变量最终值应同为2000000。然而,普通变量却出现了资源竞争性错误,两个线程都有接近一半的操作都是失败的,导致最终值仅为1123299。

而受原子保护的变量,两个线程的操作则全部成功。

名称 作用 适用内存序

重载等

从原子对象加载值

用另一个非原子值替换当前原子化的值 对象类型必须和原子对象声明时一致

memory_order_relaxed

memory_order_release

memory_order_seq_cst

load 从原子对象当中加载值(返回)

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_seq_cst

检查原子对象的锁定状态

阻塞线程至被提醒且原子值更改

用另一个原子值替换当前原子值 并返回先前的原子值

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子地比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载

(允许少部分不符合条件的值返回)

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子地比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

通知至少一个在该原子对象等待线程

notify_all【std20】

通知所有在该原子对象等待线程

[常量] is_always_lock_free

指示该类型是否始终免锁

除此之外 std::atomic 还对 int 及指针类型做了特殊化增强,以下操作函数仅适用于 int 及指针类型操作:

额外备注:C++ 20 后部分特化支持 float 。

名称 作用 适用特化类型 适用内存序

原子地将参数加到存储于原子对象的值,并返回先前保有的值 int && ptr && float(std20)

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子地从存储于原子对象的值减去参数,并获得先前保有的值 int && ptr && float(std20)

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子地进行参数和原子对象的值的逐位与,并获得先前保有的值 int

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子地进行参数和原子对象的值的逐位或,并获得先前保有的值 int

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子地进行参数和原子对象的值的逐位异或,并获得先前保有的值 int

memory_order_relaxed

memory_order_consume

memory_order_acquire

memory_order_release

memory_order_acq_rel

memory_order_seq_cst

原子值递增 int && ptr

— 原子值递减 int && ptr operator

原子值增加 int && ptr && float(std20) operator

原子值减少 int && ptr && float(std20) operator

进行原子按位与 int operator

进行原子按位或 int operator

进行原子按位异或 int

std::atomic_flag 是原子的最基本布尔类型,它是无锁的,并且它没有拷贝构造函数,也不提供 load 和 store 操作。主要用于提供比 std::atomic 更简单基本化布尔操作效率。

构造语法:

成员函数表:

名称 作用

重载等

将布尔值设置为 false

将布尔值设置为 true 并返回先前值

原子的返回当前值

阻塞线程至被提醒且原子值更改 notify_one【std20】 通知至少一个在该原子对象等待线程 notify_all【std20】 通知所有在该原子对象等待线程

2,std::memory_order

std::memory_order 指定内存访问,包括常规的非原子内存访问,如何围绕原子操作排序。在没有任何制约的多处理器系统上,多个线程同时读或写数个变量时,一个线程能观测到变量值更改的顺序不同于另一个线程写它们的顺序。其实,更改的顺序甚至能在多个读取线程间相异。一些类似的效果还能在单处理器系统上出现,因为内存模型允许编译器变换。
库中所有原子操作的默认行为提供 序列一致顺序(见后述讨论)。该默认行为可能有损性能,不过可以给予库的原子操作额外的 std::memory_order 参数,以指定附加制约,在原子性外,编译器和处理器还必须强制该操作。
— 《C++ Reference》

要理解内存序是做什么的,要先从硬件讲起:(尽量简单通俗)

以一颗 CPU i7-10875H 为例,它有8颗物理内核,从物理上来讲,它可以同时处理8条并行线程,通过超线程技术可以扩展到16条线程(物理上还是8条)。

再在软件层面来讲,并行的数千条线程是逻辑并行,终究都要交给 CPU 进行串行处理,而 CPU 可以同时处理的线程数量,就是由内核数量决定的。

而每个 CPU 内核所运算数据的存取,并不是直接存取到内存当中,而是要先经过每个内核互相独立的 L1、L2 两级高速缓存,再到 CPU 内核之间共享的 L3 高速缓存,再然后到内存。

这样就造成了一个问题,就是,假设一个内核负责的一条线程修改了某个变量的值,但是还没有刷新到内核之间共享的 L3 缓存或者内存之中,那么这时候其他 CPU 内核从内存中读取到的该变量就仍然是旧值。

所以,为了避免这种情况,这就是 std::memory_order 的作用。

首先,要明白 std::memory_order 本身是什么,它是定义于

支持传 std::memory_order 枚举的相关操作函数上文都已经列出,这里重点将这六个枚举都代表什么。

名称 作用 memory_order_relexed 只保证原子值不被其他线程同时访问,但没有线程之间同步、顺序制约,其他线程可能读取到内存当中的旧值。 memory_order_consume

[C++17注:目前不建议使用]有顺序的加载操作,只影响到当前线程。

作用是保证之后的load操作不会排在声明该枚举值的当前load操作之前。

memory_order_acquire 有顺序的加载操作,作用是保证之后所有线程的load操作不会排在声明该枚举值的当前load操作之前。 memory_order_release 有顺序的释放操作,作用是保证之后的 load(读)、store(写) 性质操作不会排在传入该枚举值的操作函数之前。 memory_order_acq_rel

有顺序整合加载(memory_order_acquire)->释放(memory_order_release)操作。

当前线程的所有 load(读)、store(写) 性质操作不会排在传入该枚举值的操作函数之前后。

所有带有释放(memory_order_release)操作同一原子对象的线程会排在传入该枚举值的操作函数之前。

而且当前线程对原子值的修改会同步给其他进行读操作的同一原子对象的线程。

memory_order_seq_cst

传入该枚举值的操作函数,load(读) 时会进行 memory_order_acquire 操作,store(写)时会进行 memory_order_release 操作。

如果是读+写就是 memory_order_acq_rel 操作。

备注:此枚举值为支持传入 std::memory_order 操作函数的缺省值。

下例演示两个线程间传递性的释放获得顺序:

下例演示三个线程间传递性的释放获得顺序:

Original: https://www.cnblogs.com/airchip/p/16390681.html
Author: 芯片烤电池
Title: C++ 标准库 std::atomic 及 std::memory_order

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/642576/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Transformer学习

    模型结构 Encoder Encoder是有N=6层的一个整体。是这6层按顺序走下来的一个整体。每层有两个子层。分别是多头自注意力和全连接前馈网络。对于每个子层,先采用残差连接,后…

    技术杂谈 2023年6月21日
    099
  • (动态规划)最长递增子列

    先来看问题, 最长递增子列。 即数组中按顺序拿出n个数,(按照原来的顺序)该子数列为递增数列。 例如:1 2 3 -1数列最后结果为3.最对应数列很显然为1 2 3 (注,只输出长…

    技术杂谈 2023年7月24日
    059
  • 密码学入门

    原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。 简介 在信息安全领域,一般会遇到”窃听”、”篡改”、…

    技术杂谈 2023年7月25日
    084
  • WEBGIS开发 Cesium中3DTiles的加载策略 LOD多层次细节 最大屏幕空间误差解析[转]

    3DTiles加载策略:3DTiles数据格式能够支持海量数据渲染的重要因素之一就是其提供了LOD能力,LOD(Levels of Detail)意译为多层次细节技术,它最重要的作…

    技术杂谈 2023年5月31日
    0101
  • python练习题:请利用Python内置的hex()函数把一个整数转换成十六进制表示的字符串

    python;gutter:true;-*- coding: utf-8 -*-请利用Python内置的hex()函数把一个整数转换成十六进制表示的字符串n1 = 255n2 = …

    技术杂谈 2023年7月24日
    072
  • R12.2常用手册

    Related Information Sources这本书包含在Oracle电子商务套件文档库中。如果该指南将您引用到其他Oracle电子商务套件文档中,只使用这些指南的最新版本…

    技术杂谈 2023年6月1日
    057
  • IOC常用的创建对象方式

    1、User.java public class User { private String name; public User() { System.out.println(&q…

    技术杂谈 2023年7月11日
    056
  • 树莓派远程连接工具VNC使用教程

    树莓派远程连接工具VNC使用教程 背景故事 树莓派作为一款迷你小主机,大部分的使用场景都会用到远程调试,远程调试用到最多的方式一般就是VNC和SSH,VNC是远程桌面型的远程方式,…

    技术杂谈 2023年7月23日
    089
  • hdu2068RPG的错排

    Problem Description 今年暑假杭电ACM集训队第一次组成女生队,其中有一队叫RPG,但做为集训队成员之一的野骆驼竟然不知道RPG三个人具体是谁谁。RPG给他机会让…

    技术杂谈 2023年5月31日
    087
  • 三层架构与MVC & 设计模式的较量

    刚刚学习了三层架构,并且正在实际应用中,但随着学习的深入,又了解到了一个叫MVC 的东西,(早在设计模式中就听到过MVC,仅仅是简单查了一下什么意思.)如今正好把这三个东西放在一起…

    技术杂谈 2023年5月31日
    069
  • maven-排查包冲突

    原因:A系统 引用B系统的功能,但是B系统有一个redis需要排除,在已经排除的情况下,还是无效,出现redis存在问题。 解决方法: 需要用到maven查询结构树 去排查 到底j…

    技术杂谈 2023年7月24日
    087
  • 搭建Redis一主两从三哨兵

    实践 – 搭建Redis一主两从三哨兵 原因: 最近在复习Redis的时候,学习到了为了提高Redis集群的 高可用性,有一个模式为 哨兵模式。 哨兵模式的作用是为了在…

    技术杂谈 2023年6月21日
    099
  • Plugin caching_sha2_password could not be loaded: The specifiedmodule could not be found.

    MySQL新版默认使用caching_sha2_password作为身份验证插件,而旧版是使用mysql_native_password 当连接MySQL时报错”plu…

    技术杂谈 2023年5月31日
    084
  • 危险的赌注

    低代码应用平台(LCAP – Low Code Application Platforms)在多样、复杂的现代软件开发情势下应运而生。根据 Gartner 的数据,Me…

    技术杂谈 2023年6月21日
    0163
  • 了解Browserify

    Browserify是一个Javascript的库,可以用来把多个Module打包到一个文件中,并且能很好地应对Modules之间的依赖关系。而Module是封装了属性和功能的单元…

    技术杂谈 2023年5月31日
    085
  • 浅谈kali : arpspoof工具原理

    介绍 arpspoof是一个通过ARP协议伪造数据包实现中间人攻击的kali工具。 中间人攻击虽然古老,但仍处于受到黑客攻击的危险中,可能会严重导致危害服务器和用户。仍然有很多变种…

    技术杂谈 2023年7月25日
    082
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球