【C++11并发】Atomic 笔记

news2024/11/24 22:28:07

简介

用atomic定义的变量,支持原子操作,即要么全部完成操作,要不全部没有完成,我们是不可能看到中间状态。一般在多线程程序中,可以用atomic来完成数据同步。
标准库为我们主要提供了四类工具

  1. atomic类模板
  2. 操作atomic的全局方法
  3. atomic_flag
  4. 内存顺序,即约束了当前atomic对象前后代码直行的相对顺序

atomic_flag是保证无锁的,任何平台都可以放心使用;atomic 对于整型,浮点类型,提供的方法会略有不同;内存顺序是在操作原子对象的时候,可以施加影响

atomic_flag

atomic_flag相当于是一个原子的bool类型,与atomic的不同点,除了无锁之外,atomic_flag提供的方法也是比较有限的,没有load和store方法:
在这里插入图片描述

构造了方法,atomic_flag有默认构造方法,但是直到C++20,默认构造时才会将状态置为false,在此之前为定义状态。所以在C++20之前都是这么定义atomic_flag的

std::atomic_flag automatic_flag = ATOMIC_FLAG_INIT;    // false

test_and_set 将atomic_flag设置true,并返回之前的值,可能是true,也可能是false

bool test_and_set( std::memory_order order = std::memory_order_seq_cst ) volatile noexcept;    // order是内存顺序,后面会写
bool test_and_set( std::memory_order order = std::memory_order_seq_cst ) noexcept;

用atomic_flag实现自旋锁是一个经典用法

#include <atomic>
 
class Spinlock
{
public:
    spinlock_mutex():m_flag(ATOMIC_FLAG_INIT) {}
    void lock() {
        while(flag.test_and_set());    // 不断重试,直到test_and_set返回false。测试m_flag
    }
    void unlock() {
        flag.clear(std::memory_order_release);
    }
private:
    std::atomic_flag m_flag;
};

std::atomic

atomic对于不同的类型支持的方法不一样,任何类型都有的方法如下:
在这里插入图片描述
构造方法

atomic() noexcept = default;
constexpr atomic( T desired ) noexcept;    // 将atomic初值设置为desired,**但这个过程不是原子的**
atomic( const atomic& ) = delete;

atomic的拷贝赋值操作和普通类的不一样,他的返回值不是引用,而是拷贝。如果返回了引用就无法保证原则操作了。另外,等号右边的是T类型,而不是atomic类型。

T operator=( T desired ) noexcept;
T operator=( T desired ) volatile noexcept;
atomic& operator=( const atomic& ) = delete;
atomic& operator=( const atomic& ) volatile = delete;

检查atomic的当前实现是否为无锁方式。如果是,则atomic原子操作是cpu指令级,否则就是通过锁实现的原子操作,可能用的就是:std::mutex

bool is_lock_free() const noexcept;
bool is_lock_free() const volatile noexcept;

向atomic中写值,为原子操作

void store( T desired, std::memory_order order = std::memory_order_seq_cst ) noexcept;
void store( T desired, std::memory_order order = std::memory_order_seq_cst ) volatile noexcept;

从atomic中读取值,为原子操作。返回值为atomic中保存值的拷贝

T load( std::memory_order order = std::memory_order_seq_cst ) const noexcept; 
T load( std::memory_order order = std::memory_order_seq_cst ) const volatile noexcept;

隐式转换,等价于load()

operator T() const noexcept;
operator T() const volatile noexcept;

读改写当前变量,返回值是读到的之前的值,改是指对atomic中管理值的修改(瞎猜的:会不会是写缓存,后面研究一下 ),写是指写内存。

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

比较修改当前变量
如果*this == expected, *this = desired,return true
否则 expected = *this, return false

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

weak版本的CAS允许偶然出乎意料的返回(比如在字段值和期待值一样的时候却返回了false),不过在一些循环算法中,这是可以接受的。通常它比起strong有更高的性能。

atomic对整型支持的最好的,提供的方法也是最多的,还提供了很多typedef的别名,类似这样
在这里插入图片描述
对于整型atomic提供的额外方法有:
在这里插入图片描述
加法会返回之前的值

T fetch_add( T arg, std::memory_order order = std::memory_order_seq_cst ) noexcept;

加法也会返回之前的值

T fetch_sub( T arg, std::memory_order order = std::memory_order_seq_cst ) noexcept;

按位与,或,异或,也都会返回之前的值

如果atomic的类型为T*,那么直行加,减方法的时候,会返回T*

如果使用自定义类型去作为atomic的模板参数,需要这个自定义类型是可平凡复制的(TriviallyCopyable ),即下面对象的value都必须为true

std::is_trivially_copyable<T>::value
std::is_copy_constructible<T>::value
std::is_move_constructible<T>::value
std::is_copy_assignable<T>::value
std::is_move_assignable<T>::value

简单来说,可平凡复制类型表示这个类型的对象可以直接进行内存拷贝,而不需要执行特殊的拷贝构造函数或者析构函数。换句话说,可平凡复制类型的对象可以拷贝到char或者unsigned char数组中,可以通过std::memcpy or std::memmove操作。一个可平凡复制类型通常具有简单的内存布局,没有虚拟函数或者虚拟基类,并且其拷贝构造函数和析构函数是默认生成的。这意味着这种类型的对象可以更高效地进行拷贝和赋值操作。像标量类型(算术类型/枚举/指针),标量类型作为成员变量构成的类,以及相应数组和cv-qualified修饰的,都是平凡复制类型。
这里有一些例子:https://www.cnblogs.com/BuzzWeek/p/17578402.html

cv-qualified
CV分别是const 以及volatile的缩写。
用const,volatile以及const volatile之一作修饰的变量被成为cv-qualified ,否则该变量是cv-unqualified

内存顺序 std::memory_order

在atomic的很多方法中都有一个参数为std::memory_order类型,不过一般都有默认值(memory_order_seq_cst)。现代C++编译器为了进一步提高代码运行效率,在编译的时候,可能会改变代码的直行顺序,当然是在不改变原有代码逻辑的情况下。比如下面例子,不管那种顺序直行代码,都不影响最终结果。但改变顺序后,可能直行效率会更高一些。std::memory_order正是用来告诉编译器,要不要做这种优化,当然C++中定义会更加详细一些。

int a = 1;
int b = 2;
int c = a;
---
int b = 2;
int a = 1;
int c = a;

std::memory_order的定义如下

typedef 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;
枚举值含义
memory_order_relaxed允许编译器为了效率可以任意优化执行顺序
memory_order_consume如果后续有关于当前原子变量的操作,必须在本条原子操作完成之后直行
memory_order_acquire所有后续的读操作必须在本条原子操作完成后直行
memory_order_release所有之前的写操作完成后才能执行本条原子操作
memory_order_acq_relmemory_order_acquire 和 memory_order_release 的组合
memory_order_seq_cst全部存取都按顺序执行,不允许编译器优化直行顺序

比如使用 memory_order_consume

atomic<int*> ptr;
atomic<int> data;

{
	int* p2;
	while(!(p2=ptr.load(memory_order_consume)));
	assert(*p2=="Hello");    // 这一行一定在while之后
	int d = 5;    // 这一行可能在int* 2之前直行,也可能在while之前,总之由于他不依赖ptr,所以就有可能被编译器优化执行顺序
}

再比如 memory_order_acquire

atomic<int> a;
atomic<int> b;

{
	while(b.load(memory_order_acquire)!=2);//本原子操作必须完成才能执行之后所有的读原子操作, 即a.load一定在本条语句之后
	std::cout << a.load(memory_order_relaxed) << std::endl;
}

其他的基本类似

前面提到std::memory_order一般是作为atomic相关的方法的一个参数。那么如果想脱离atomic使用std::memory_order,C++提供了std::atomic_thread_fence

extern "C" void atomic_thread_fence( std::memory_order order ) noexcept;

一个官方的例子

// Global
std::string computation(int);
void print(std::string);
 
std::atomic<int> arr[3] = {-1, -1, -1};
std::string data[1000]; //non-atomic data
 
// Thread A, compute 3 values.
void ThreadA(int v0, int v1, int v2)
{
//  assert(0 <= v0, v1, v2 < 1000);
    data[v0] = computation(v0);
    data[v1] = computation(v1);
    data[v2] = computation(v2);
    std::atomic_thread_fence(std::memory_order_release);
    std::atomic_store_explicit(&arr[0], v0, std::memory_order_relaxed);
    std::atomic_store_explicit(&arr[1], v1, std::memory_order_relaxed);
    std::atomic_store_explicit(&arr[2], v2, std::memory_order_relaxed);
}
 
// Thread B, prints between 0 and 3 values already computed.
void ThreadB()
{
    int v0 = std::atomic_load_explicit(&arr[0], std::memory_order_relaxed);
    int v1 = std::atomic_load_explicit(&arr[1], std::memory_order_relaxed);
    int v2 = std::atomic_load_explicit(&arr[2], std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);
//  v0, v1, v2 might turn out to be -1, some or all of them.
//  Otherwise it is safe to read the non-atomic data because of the fences:
    if (v0 != -1)
        print(data[v0]);
    if (v1 != -1)
        print(data[v1]);
    if (v2 != -1)
        print(data[v2]);
}

操作atomic的全局方法

功能和atomic的成员方法一样,命名风格基本都是 atomic_xxx

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ZStack Cloud云平台服务中国化学工程集团高性能数据库

中国化学工程通过ZStack Cloud云平台构建云基础设施&#xff0c;并为其提供高性能、高可用的云主机、云存储和云网络服务&#xff1b;并通过ZStack Cloud云平台弹性裸金属能力ZStack RDS数据库云平台&#xff0c;满足高性能数据库和多种数据库统一管理的需求&#xff1b;此外&a…

DOS 批处理 (二)

DOS 批处理 1. 基础 DOS 命令1.1 基础命令1.2 文件系统操作1.3 文件夹管理1.4 文件管理1.5 网络相关1.6 系统管理1.7 IF、FOR和NETIFFORNET 1. 基础 DOS 命令 command /? 查找帮助DOS命令不区分命令字母的大小写 C:\Users\Administrator>echo 1 1 C:\Users\Administrator…

我们一起聊一聊JWT的那些事

我们一起聊一聊JWT的那些事 一切美好&#xff0c;如期而至… 什么是JWT JWT&#xff0c;全称为 JSON Web Token&#xff0c;是一种用于在网络上安全地传递信息的开放标准&#xff08;RFC 7519&#xff09;。JWT 是一种紧凑且独立的方式&#xff0c;用于在各方之间以 JSON 对象…

Docker部署开源分布式任务调度系统DolphinScheduler与远程访问办公

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…

STM32 定时器配置步骤

定时中断基本框架结构图&#xff1a; 根据结构图可按步骤配置定时器 第1步&#xff1a;RCC开启时钟。打开时钟后定时器的基准时钟和整个外设的工作时钟就会同时打开。 第2步&#xff1a;选择时基单元的时钟源。对于定时中断可选择内部时钟源. 第3步&#xff1a;配置时基单元…

12.7作业

1.头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QIcon> #include <QLabel> #include <QMovie> #include <QLineEdit> #include <QPushButton> class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidge…

论文笔记——DAS

论文&#xff1a;https://arxiv.org/pdf/2311.12091.pdf 代码&#xff1a;暂无 卷积神经网络&#xff08;CNNs&#xff09;在局部空间模式识别方面表现出色。对于许多视觉任务&#xff0c;如物体识别和分割&#xff0c;显著信息也存在于CNN核边界之外。然而&#xff0c;由于CN…

在Spring Cloud使用Hystrix核心组件,并注册到Eureka注册中心去

其实吧&#xff0c;写Spring Cloud系列&#xff0c;我有时候觉得也挺难受的&#xff0c;因为Spring Cloud的微服务启动都需要一个一个来&#xff0c;并且在IDea中也需要占用比较大的内存&#xff0c;并且我本来可以一篇写完5大核心组件的&#xff0c;但是我却分了三篇&#xff…

Implicit Neural Representation for Cooperative Low-light Image Enhancement

GitHub - Ysz2022/NeRCo: [ICCV 2023] Implicit Neural Representation for Cooperative Low-light Image Enhancement 参考&#xff1a;ICCV2023 | 将隐式神经表征用于“低光增强”&#xff0c;北大张健团队提出NeRCo (qq.com) 以下三个因素限制了现有低光图像增强方法的应用…

状态机的练习:按键控制led灯

设计思路&#xff1a; 三个按键控制led输出。 三个按键经过滤波(消抖)&#xff0c;产生三个按键标志信号。 三个led数据的产生模块&#xff08;流水&#xff0c;跑马&#xff0c;闪烁模块&#xff09;&#xff0c;分别产生led信号。 这六路信号&#xff08;三路按键信号&am…

Java网络编程——Socket用法解析

在客户/服务器通信模式中&#xff0c;客户端需要主动创建与服务器连接的Socket&#xff0c;服务器端收到了客户的连接请求&#xff0c;也会创建与客户连接的Socket。Socket可以被看作是通信连接两端的收发器&#xff0c;服务器与客户都通过套接字来收发数据。 1、构造Socket …

企业数字化转型的七个成功案例

尽管经济形势严峻&#xff0c;但60%的企业告诉波士顿咨询公司&#xff0c;他们将在2023年增加数字化转型投资。根据Precedence Research最近的一份报告&#xff0c;到2025年&#xff0c;数字化转型市场预计将达到1.458万亿美元。 成长型股权公司PSG的董事总经理安东尼爱德华兹…

【hacker送书第10期】AI时代系列丛书(五选一)

AI时代系列丛书 AI时代程序员开发之道✨内容简介参与方式 AI时代项目经理成长之道✨内容简介参与方式 AI时代架构师修炼之道✨内容简介参与方式 AI时代产品经理升级之道✨内容简介参与方式 AI时代Python量化交易实战✨内容简介参与方式 AI时代程序员开发之道✨ 内容简介 本书是…

C#中GDI+图形图像技术(Graphics类、Pen类、Brush类)

目录 一、创建Graphics对象 1.创建Pen对象 2.创建Brush对象 &#xff08;1&#xff09;SolidBrush类 &#xff08;2&#xff09;HatchBrush类 ​​​​​​​&#xff08;3&#xff09;LinerGradientBrush类 用户界面上的窗体和控件非常有用&#xff0c;且引人注目&#…

优雅提效:Guava的字符串处理工具

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;今天咱们要聊一聊Google Guava这个超棒的Java库&#xff0c;尤其是它的字符串处理工具。对于Java程序员来说&#xff0c;字符串处理是日常工作的一部分&#xff0c;而Guava在这方面提供了非常强大的支持。使用Gu…

Unity中Batching优化的GPU实例化(3)

文章目录 前言一、UNITY_SETUP_INSTANCE_ID(v);二、在UnityInstancing.cginc文件中&#xff0c;看一下Unity这句话做了什么1、使用了该 .cginc 后&#xff0c;会自动预定义该函数2、需要满足GPU实例化条件&#xff0c;才会执行对应语句3、满足GPU实例化后&#xff0c;主要执行的…

Python Tornado 框架的终极指南!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python Tornado框架是一个高性能的异步Web框架&#xff0c;被广泛应用于构建实时Web应用和API服务。本文将深度解析Tornado框架&#xff0c;介绍其核心概念、异步特性、路由系统、模板引擎以及WebSocket支持等方…

通过Powershell远程控制windows服务器

1、被测服务器5985、5986端口开启&#xff0c;在网络设置中&#xff0c;点击“更改连接属性”。 2、网络配置文件中选择“专用”。 3、以管理员权限运行Powershell&#xff0c; 4.通过powershell命令在本地电脑上添加远端信任主机 winrm set winrm/config/client {TrustedHos…

世微 AP3266 大功率同步降压恒流芯片 过EMC 车灯驱动

产品描述 AP3266 是高效率、外围简单、内置功率管的同步降压恒流芯片&#xff0c;适用于4-40V输入的降压LED恒流驱动芯片。输出最大功率可达 40W&#xff0c;最大电流3.6A。AP3266 可通过调节 OVP 端口的分压电阻&#xff0c;设定输出空载电压 保护&#xff0c;避免高压 空载上…

如何搭建一套完整的智能安防视频监控平台?关于设备与软件选型的几点建议

安防视频监控系统主要由前端摄像机设备、视频显示设备、视频存储设备、安防应用软件/平台以及其它传输、辅助类设备组成。一般来说&#xff0c;安防监控系统具有可扩展和开放性&#xff0c;以方便未来的扩展和与其他系统的集成。今天我们就来介绍一下&#xff0c;搭建一套完整的…