C++11:原子交换函数compare_exchange_weak和compare_exchange_strong

2023-05-16

 

我们知道在C++11中引入了mutex和方便优雅的lock_guard。但是有时候我们想要的是性能更高的无锁实现,下面我们来讨论C++11中新增的原子操作类Atomic,我们可以利用它巧妙地实现无锁同步。

CAS(Compare and Swap)是个原子操作,保证了如果需要更新的地址没有被他人改动多,那么它可以安全的写入。而这也是我们对于某个数据或者数据结构加锁要保护的内容,保证读写的一致性,不出现dirty data。现在几乎所有的CPU指令都支持CAS的原子操作。

 

Atomic

C++11给我们带来的Atomic一系列原子操作类,它们提供的方法能保证具有原子性。这些方法是不可再分的,获取这些变量的值时,永远获得修改前的值或修改后的值,不会获得修改过程中的中间数值。

 这些类都禁用了拷贝构造函数,原因是原子读和原子写是2个独立原子操作,无法保证2个独立的操作加在一起仍然保证原子性。

atomic<T>提供了常见且容易理解的方法:

  1. store
  2. load
  3. exchange
  4. compare_exchange_weak
  5. compare_exchange_strong

       store是原子写操作,而load则是对应的原子读操作。

       exchange允许2个数值进行交换,并保证整个过程是原子的。

       而compare_exchange_weak和compare_exchange_strong则是著名的CAS(compare and set)。参数会要求在这里传入期待的数值和新的数值。它们对比变量的值和期待的值是否一致,如果是,则替换为用户指定的一个新的数值。如果不是,则将变量的值和期待的值交换。

compare_exchange_strong:atomic库中的一个函数,入参是3个,expect,desire,memoryorder,意思是如果当前的变
this的值==expect值,则将this值改为desire,并返回true,否则,返回false,不进行修改,即进行一个读的操作。通常用于例如线程B等待线程A执行完毕,或者执行到某个步骤。此时线程B可以进行while等待,线程A在执行到对应步骤,将对应的原子变量置为expect值即可。类似于“接力运动”。这里由于会进行读写操作,所以,memory order一般是acq rel,而A线程由于要保证都执行完毕,执行顺序没有关系,所以一般是Release的memory order。

 

compare_exchange_weak和compare_exchange_strong

 C++11中CAS实现:

template< class T>
struct atomic<T*>
{
public:
bool compare_exchange_weak( T& expected, T desired,
                            std::memory_order success,
                            std::memory_order failure );
bool compare_exchange_weak( T& expected, T desired,
                            std::memory_order success,
                            std::memory_order failure ) volatile;
bool compare_exchange_weak( T& expected, T desired,
                            std::memory_order order =
                                std::memory_order_seq_cst );
bool compare_exchange_weak( T& expected, T desired,
                            std::memory_order order =
                                std::memory_order_seq_cst ) volatile;
bool compare_exchange_strong( T& expected, T desired,
                              std::memory_order success,
                              std::memory_order failure );
bool compare_exchange_strong( T& expected, T desired,
                              std::memory_order success,
                              std::memory_order failure ) volatile;    
bool compare_exchange_strong( T& expected, T desired,
                              std::memory_order order =
                                  std::memory_order_seq_cst );
bool compare_exchange_strong( T& expected, T desired,
                              std::memory_order order =
                                  std::memory_order_seq_cst ) volatile;
...
};

 当前值与期望值(expect)相等时,修改当前值为设定值(desired),返回true
 当前值与期望值(expect)不等时,将期望值(expect)修改为当前值,返回false

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

 

实例:

       在非并发条件下,要实现一个栈的Push操作,我们可能有如下操作:

    1. 新建一个节点
    2. 将该节点的next指针指向现有栈顶
    3. 更新栈顶    

       但是在并发条件下,上述无保护的操作明显可能出现问题。下面举一个例子:

  1. 原栈顶为A。(此时栈状态: A->P->Q->...,我们约定从左到右第一个值为栈顶,P->Q代表p.next = Q)
  2. 线程1准备将B压栈。线程1执行完步骤2后被强占。(新建节点B,并使 B.next = A,即B->A)
  3. 线程2得到cpu时间片并完成将C压栈的操作,即完成步骤1、2、3。此时栈状态(此时栈状态: C->A->...)
  4. 这时线程1重新获得cpu时间片,执行步骤3。导致栈状态变为(此时栈状态: B->A->...)

       结果线程2的操作丢失,这显然不是我们想要的结果。

那么我们如何解决这个问题呢?

只要保证步骤3更新栈顶时候,栈顶是我们在步骤2中获得顶栈顶即可。因为如果有其它线程进行操作,栈顶必然改变。

我们可以利用CAS轻松解决这个问题:如果栈顶是我们步骤2中获取顶栈顶,则执行步骤3。否则,自旋(即重新执行步骤2)。

 1 template<typename T>
 2 class lock_free_stack
 3 {
 4 private:
 5   struct node
 6   {
 7     T data;
 8     node* next;
 9 
10     node(T const& data_): 
11      data(data_)
12     {}
13   };
14 
15   std::atomic<node*> head;
16 public:
17   void push(T const& data)
18   {
19     node* const new_node=new node(data); 
20     new_node->next=head.load();  //如果head更新了,这条语句要春来一遍
21     while(!head.compare_exchange_weak(new_node->next,new_node));
22   }
23 };

   我们可以注意到一个非常巧妙的设计。在push方法里,atomic_compare_exchange_weak如果失败,证明有其他线程更新了栈顶,而这个时候被其他线程更新的新栈顶值会被更新到new_node->next中,因此循环可以直接再次尝试压栈而无需由程序员更新new_node->next。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11:原子交换函数compare_exchange_weak和compare_exchange_strong 的相关文章

  • allocator简单实现

    allocator是c 43 43 标准库中用于管理内存的一个类 主要包括以下类方法 xff1a 代码如下 xff08 来源于 STL源码剖析 xff09 xff1a ifndef JJALLOC define JJALLOC includ
  • ROS2学习笔记(十一)-- ROS2 bag数据记录与回放

    简介 xff1a ROS2提供了ros2 bag命令 xff0c 可以记录指定主题的数据到文件中 xff0c 也可以将记录下的内容再发布出来 xff0c 相当于是数据的回放 xff0c 除了通过命令行的方式实现数据记录以外 xff0c 也可
  • Altium Designer 10 导出文件(PDF,gerber,BOM)

    作者 xff1a 卢老师 华清远见嵌入式学院讲师 5 导出原理图文档 GERBER 文件 xff0c BOM对于导出 PDF 文档 xff0c 多人分析时 xff0c 不能保证所有的电脑都安装有 AD10 软件 xff0c 这个也很有必要
  • 纯C++的Socket访问Http封装类

    纯C 43 43 的Socket访问Http封装类 1 项目中要使用c 43 43 43 43 来访问Web服务器 xff0c 从网上找了个C 43 43 的封装类 xff0c 其中调用了MFC xff0c 在VC2005上用能用 xff0
  • 一个C++解析HTML的库

    HTTP协议使用广泛 xff0c 相应的 xff0c C 43 43 在这块需求也开始增加 一个好的解析库可以达到事半功倍的效果 xff0c 在此贴出我的解析库的代码 xff0c 方便新手朋友们使用 hHttpParse h ifndef
  • Intel Realsense D435i SDK Install

    Intel Realsense D435i 0 引言1 参考2 Install3 Other 0 引言 自己买的还没到 xff0c 借的同学 xff0c 生怕给他搞坏了 1 参考 ref0 官方giahubref1 官方Installref
  • OBVP问题推导

    OBVP问题推导 0 引言1 推导2 code 0 引言 记录一下 时间紧张 xff0c 不整理了 xff0c 直接上草稿纸 一元四次方程求解参考博客 1 推导 2 code 代码完全按照推导书写 xff0c 包括变量的命令 span cl
  • CMakeLists中的add_definitions()函数

    CMakeLists中的add definitions函数 0 引言1 add definitions 2 应用 0 引言 其实这个函数在安装一些库的时候 xff0c 它的CMakeLists里面就有这样的函数 典型的就是opencv了 o
  • Intel Realsense D435i&L515 驱动安装

    Intel Realsense D435i amp L515 驱动安装 0 引言1 D435i amp L515固件更新1 1 D435i固件更新1 2 L515固件更新 2 Intel Realsense驱动安装3 ROS Wrapper
  • 位置编码Positional Encoding

    位置编码Positional Encoding 1 Transformers中的PE2 什么是Transformer位置编码2 1 表格型2 2 相对位置的关系 函数型 3 为什么可以表示相对距离 xff1f 4 其他参考 内容全来自于网络
  • vscode 调试 Python 代码

    vscode 调试 Python 代码 0 引言1 插件2 环境布置3 parser解析 0 引言 参考0参考1 1 插件 官方的python插件代码助手 xff0c 自动补全 xff1a 解释器选择 xff0c 在窗口右下角选择解释器 x
  • OpenCV 相机转换为 OpenGL 相机

    OpenCV 相机转换为 OpenGL 相机 0 引言1 预备知识和概述2 资源3 OpenCV和OpenGL中的图像坐标系统3 1 OpenCV H Z和OpenGL中的主轴3 2 齐次坐标和OpenGL中的归一化设备坐标 4 OpenC
  • 两轮差速小车循线控制原理分析

    硬件资料设定 xff1a 小车驱动来自于两个相同的电机 xff0c 转向依靠两轮差速实现 xff0c 小车前后左右安装超声波传感器 xff0c 前后各一个 xff0c 左右各两个 xff1b 功能目标 xff1a 假设小车左侧有墙壁 xff
  • ch06-Pytorch的正则化与归一化

    ch06 Pytorch的正则化与归一化 0 引言1 weight decay 和 dropout1 1 Regularization1 2 Dropout 2 Normalization2 1 Batch Normalization2 2
  • ch07-Pytorch的训练技巧

    ch07 Pytorch的训练技巧 0 引言1 模型保存与加载1 1 序列化与反序列化1 2 PyTorch 中的模型保存与加载1 3 模型的断点续训练 2 模型 Finetune2 1 Transfer Learning amp Mode
  • opencv-contrib-Python编译module 'cv2.cv2' has no attribute 'xfeatures2d'

    opencv contrib Python编译module 39 cv2 cv2 39 has no attribute 39 xfeatures2d 39 引言解决步骤一解决步骤二 引言 opencv contrib Python编译出现
  • find_package()函数

    find package函数 引言1 find package用法2 find package原理3 A required library with LAPACK API not found 错误解决4 添加findpackage查询路径
  • py安装文件时报错usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]

    py安装文件时报错usage setup py global opts cmd1 cmd1 opts cmd2 cmd2 opts 引言solved 引言 报错 xff1a python setup py fastentrypoints u
  • VScode单步调试

    VScode配置 0 快捷键1 安装clang2 VScodeDebug3 Cmake支持gdb调试的方法 0 快捷键 稍大工程在vscode下的调试参考该博客 Ctrl 43 打开默认终端 Ctrl 43 Shift 43 新建新的终端
  • 串口通信简介

    串口通信 串口通信是一种串行异步通信 xff0c 通信双方以字符帧作为数据传输单位 xff0c 字符帧按位依次传输 xff0c 每个位占固定的时间长度 两个字符帧之间的传输时间间隔可以是任意的 xff0c 即传输完一个字符帧之后 xff0c

随机推荐

  • ubuntu16.0 ROS(介绍EAI的YDLIDAR-X4激光雷达在ROS下使用方法)

    YDLIDAR X4激光雷达介绍 YDLIDAR X4激光雷达是深圳越登智能科技有限公司 xff08 YDLIDAR xff0c 这家公司属于EAI xff09 研发的一款 360 度二维测距产品 xff0c 本产品基于三角测距原理 xff
  • php使用http_build_query,parse_url,parse_str创建与解析url

    1 http build query http build query 可以创建urlencode之后的请求字符串 span class hljs keyword string span http build query mixed spa
  • 无人驾驶小车调试笔记(六)-- 车轮校准

    简介 xff1a 小车的动力完全来自于两个电机带动的车轮 xff0c 在理想状态下 xff0c 给两个电机同样的驱动参数 xff0c 两个车轮会以同样的转速带动小车直线行驶 xff0c 而实际情况是每个电机可能都会有个体差异 xff0c 也
  • Nginx HTTP详解

    正文 1 Nginx启动流程 2 HTTP 初始化 新连接建立时的行为 在上次博客的最后可以看到 xff0c 在ngx event accept方法建立连接的最后一步 xff0c 将会调用ngx listening t监听结构体的handl
  • 时钟周期,机器周期,指令周期的相互关系

    1 时钟周期 61 振荡周期 xff0c 名称不同而已 xff0c 都是等于单片机晶振频率的倒数 xff0c 如常见的外接12M晶振 xff0c 那它的时钟周期 61 1 12M 2 机器周期 xff1a 8051系列单片机的机器周期 61
  • 单片机的分频是什么意思?

    分频就是单片机的时钟频率 xff08 也就是晶振的震荡频率 xff09 F经过12分频 xff0c 变换成F 12的频率 简单的来说就是以整数倍降低频率 2分频就是分频前的频率除以2 xff1b 4分频就是分频前的频率除以4 比如 xff1
  • NMOS和PMOS管

    这里我先说一下我自己分辨MOS管的方法 对于NMOS我们看下图中的箭头 xff0c 都是远离源头 对于PMOS我们看箭头 xff0c 都是指向源头 P xff1a POSITIVE积极的寻找自己的起源 N xff1a NEGTIVE消极的远
  • 基本运算放大电路

    我先说明 下面的内容应该很多人都看到过 xff0c 但是我建议还是细看 xff0c 最好自己推一下 我就是这么做的 运算放大器工作原理综述 xff1a 运算放大器组成的电路五花八门 xff0c 令人眼花瞭乱 xff0c 在分析运算放大器工作
  • PCB板框的绘制——AD19

    pcb板框的绘制当然首先要切换到keep out 层才行 找到设置 xff0c 找到keep out 假如我们要绘制一个矩形的板框 xff0c 我们选择线径就可以 手动绘制一个矩形的板框 我们需要让我们的板子边框按照我们所绘制的走线来定义
  • 零基础自学STM32-野火——GPIO复习篇——使用绝对地址操作GPIO

    今天主要是复习一下 结合野火的 零基础开发指南 名字没记住大概是这个 先放一张结构图 存储器映射 xff08 初学重点 xff09 xff1a 我们的片内外设比如 xff1a Flash Sram Fsmc 以及挂在AHB 总线上的外设 x
  • Lcd1602——斌哥51

    最新修改时间2022 7 22 LCD1602 16代表显示16个字符 xff0c 2代表总共显示两行 芯片的工作电压是4 5 5 5v 工作电流2 0ma xff08 5V xff09 模块最佳工作电压5 0v 字符尺寸 xff1a 2
  • 无人驾驶小车调试笔记(七)-- 相机校准

    简介 xff1a 在第五节的内容中 xff0c 我们学习了使用rqt工具集观看摄像头视频流的方法 xff0c 细心的同学应该会发现camera node发布的视频数据中的图像有变形现象 xff0c 图像变形会导致直线不直 xff0c 部分区
  • Python实现MySql、SqlServer增删改查操作

    span class token keyword import span pymssql span class token keyword def span span class token function connection sql
  • ds1302——斌哥51

    以下内容分别借鉴了 清翔 51 xff0c 斌哥51 xff0c 以及CSDN 普通的不普通少年 内部结构 xff1a DS1302 包括时钟 日历寄存器和 31 字节 xff08 8 位 xff09 的数据暂存寄存器 xff0c 数据通信
  • AD添加LOGO

    先上原文链接 xff1a http www allchiphome com circuit pcb logo creator http www allchiphome com circuit pcb logo creator http ww
  • 视频播放组件实战【LivePlayer H5播放器】

    在公司项目开发中 xff0c 有一个项目里面需要做一个视频播放的功能 xff0c 播放方式是调用海康平台提供的接口获取流地址来进行视频的播放并且最重要的是需要支持flash 由于前端用的Vue xff0c 对比了几个 xff0c 最后选择了
  • 如何用示波器测量串口

    如何确定时基 假如要测量的波特率为9600 则每一比特位的时间为 xff1a 1 9600 104 s xff0c 一般示波器横向上每个大格子里5个小格子 xff0c 要想看清一比特位一般需要一个小格子就够了 xff0c 则时基为 xff1
  • Keil使用命令行附加预定义宏编译

    1 前言 很多时候 xff0c 一份Keil工程代码可能需要满足多个不同的应用场景 可以通过逻辑判断 xff0c 将多个不同的点集成在一份代码之中 xff0c 但是嵌入式往往特别关注RAM空间 xff0c 集成过多的逻辑判断 xff0c R
  • Python的函数装饰器,@staticmethod、@classmethod 和 @property

    什么是Python 的 函数装饰器 xff1f Python 内置的 3 种函数装饰器 xff0c 分别是 xff20 staticmethod xff20 classmethod 和 64 property 那么 xff0c 函数装饰器的
  • C++11:原子交换函数compare_exchange_weak和compare_exchange_strong

    我们知道在C 43 43 11中引入了mutex和方便优雅的lock guard 但是有时候我们想要的是性能更高的无锁实现 xff0c 下面我们来讨论C 43 43 11中新增的原子操作类Atomic xff0c 我们可以利用它巧妙地实现无