比较并交换(compare and swap, CAS),是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。 该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值。

概述[编辑]

一个CAS操作的过程可以用以下c代码表示: [1]

1 int cas(long *addr, long old, long new)
2 {
3 /* Executes atomically. */
4 if(*addr != old)
5 return 0;
6 *addr = new;
7 return 1;
8 }

在使用上,通常会记录下某块内存中的旧值,通过对旧值进行一系列的操作后得到新值,然后通过CAS操作将新值旧值进行交换。如果这块内存的值在这期间内没被修改过,则旧值会与内存中的数据相同,这时CAS操作将会成功执行 使内存中的数据变为新值。如果内存中的值在这期间内被修改过,则一般[2]来说旧值会与内存中的数据不同,这时CAS操作将会失败,新值将不会被写入内存。

应用[编辑]

在应用中CAS可以用于实现无锁数据结构,常见的有无锁队列(先入先出)[3] 以及无锁堆(先入后出)。对于可在任意位置插入数据的链表以及双向链表,实现无锁操作的难度较大。[4]

ABA问题[编辑]

ABA问题是无锁结构实现中常见的一种问题,可基本表述为:

  1. 进程P1读取了一个数值A
  2. P1被挂起(时间片耗尽、中断等),进程P2开始执行
  3. P2修改数值A为数值B,然后又修改回A
  4. P1被唤醒,比较后发现数值A没有变化,程序继续执行。

对于P1来说,数值A未发生过改变,但实际上A已经被变化过了,继续使用可能会出现问题。在CAS操作中,由于比较的多是指针,这个问题将会变得更加严重。试想如下情况:

   top
|
V
0x0014
| Node A | --> | Node X | --> ……

有一个堆(先入后出)中有top和节点A,节点A目前位于堆顶top指针指向A。现在有一个进程P1想要pop一个节点,因此按照如下无锁操作进行

pop()
{
do{
ptr = top; // ptr = top = NodeA
next_prt = top->next; // next_ptr = NodeX
} while(CAS(top, ptr, next_ptr) != true);
return ptr;
}

而进程P2在执行CAS操作之前打断了P1,并对堆进行了一系列的pop和push操作,使堆变为如下结构:

   top
|
V
0x0014
| Node C | --> | Node B | --> | Node X | --> ……

进程P2首先pop出NodeA,之后又Push了两个NodeB和C,由于内存管理机制中广泛使用的内存重用机制,导致NodeC的地址与之前的NodeA一致。

这时P1又开始继续运行,在执行CAS操作时,由于top依旧指向的是NodeA的地址(实际上已经变为NodeC),因此将top的值修改为了NodeX,这时堆结构如下:

                                   top
|
0x0014 V
| Node C | --> | Node B | --> | Node X | --> ……

经过CAS操作后,top指针错误的指向了NodeX而不是NodeB。

实现[编辑]

CAS操作基于CPU提供的原子操作指令实现。对于Intel X86处理器,可通过在汇编指令前增加LOCK前缀来锁定系统总线,使系统总线在汇编指令执行时无法访问相应的内存地址。而各个编译器根据这个特点实现了各自的原子操作函数。[5]

  • C语言C11的头文件<stdatomic.h>。由GNU提供了对应的__sync系列函数完成原子操作。 [6][7]
  • C++11,STL提供了atomic系列函数。[8][7]
  • JAVA,sun.misc.Unsafe提供了compareAndSwap系列函数。[9]
  • C#,通过Interlocked方法实现。[10]
  • Go, 通过import "sync/atomic"包实现。[11]
  • Windows,通过Windows API实现了InterlockedCompareExchangeXYZ系列函数。[12][7]

比较并交换(compare and swap, CAS)的更多相关文章

  1. Compare and Swap [CAS] 算法

    一个Java 5中最好的补充是对原子操作的支持类,如AtomicInteger,AtomicLong等.这些类帮助你减少复杂的(不必要的)多线程代码,实际上只是完成一些基本操作,如增加或减少多个线程之 ...

  2. Compare and Swap(CAS)

    CAS(Compare and Swap)是个原子操作.拿到一个新值后,CAS将其与内存中的值进行比较,若内存中的值和这个值不一样,则将这个值写入内存,否则,不做操作.在Java的 java.util ...

  3. 非阻塞同步算法与CAS(Compare and Swap)无锁算法

    锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的.内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放. ...

  4. 【Java并发编程】9、非阻塞同步算法与CAS(Compare and Swap)无锁算法

    转自:http://www.cnblogs.com/Mainz/p/3546347.html?utm_source=tuicool&utm_medium=referral 锁(lock)的代价 ...

  5. Java并发基础之Compare And Swap/Set(CAS)

    什么是 CAS?CAS(Compare And Swap/Set)比较并交换, CAS 算法的过程是这样:它包含 3 个参数CAS(V,E,N). V 表示待更新的变量(内存值), E 表示预期值(旧 ...

  6. CAS(Compare and Swap)理解

    什么叫CAS(Compare and Swap)?  硬件同步原语!! 什么蛋疼的名字,一般人很难理解.根据英文全称翻译==比较与交换,这个名字大致还能理解一点,目前先暂且这么理解吧. 有啥用处? 对 ...

  7. Java多线程和并发(十一),CAS(Compare and Swap)

    目录 1.CAS简介 2.CAS多数情况下对开发者来说是透明的 3.CAS缺点 十一.CAS(Compare and Swap) Synchronized直观意义上是一种悲观锁 cas则是乐观锁的一种 ...

  8. 无锁机制----比较交换CAS Compare And Swap

    一.锁与共享变量 加锁是一种悲观的策略,它总是认为每次访问共享资源的时候,总会发生冲突,所以宁愿牺牲性能(时间)来保证数据安全. 无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加 ...

  9. 面试:为了进阿里,又把并发CAS(Compare and Swap)实现重新精读一遍

    该系列文章已收录在公众号[Ccww技术博客],原创技术文章第一时间推出 前言 在面试中,并发线程安全提问必然是不会缺少的,那基础的CAS原理也必须了解,这样在面试中才能加分,那来看看面试可能会问那些问 ...

  10. CAS(Compare and Swap)无锁算法-学习笔记

    非阻塞同步算法与CAS(Compare and Swap)无锁算法 这篇问题对java的CAS讲的非常透彻! 锁的代价 1. 内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的 ...

随机推荐

  1. Linux: rsyslog.conf 配置

    refer to: https://www.debian.org/doc/manuals/debian-handbook/sect.syslog.en.html 日志子系统 Each log mess ...

  2. linux 问题: ssh登录报错,ssh_exchange_identification,多次几次可以登录

    分析 怀疑是句柄数不够,和ssh的最大登录限制 确认 2.1 确认句柄数 过程: ~# systemctl status sshd | grep -i pid Main PID: 3767395 (s ...

  3. [自然语言处理] 自然语言处理库spaCy使用指北

    spaCy是一个基于Python编写的开源自然语言处理库.基于自然处理领域的最新研究,spaCy提供了一系列高效且易用的工具,用于文本预处理.文本解析.命名实体识别.词性标注.句法分析和文本分类等任务 ...

  4. vlunhub笔记(一)deathnote

    (一)查询靶机ip 通过 arp-scan -l 查询目标靶机ip,查询结果如下图红框所示. (二)信息收集 直接访问目标ip,发现无法访问,只返回网站域名. 网站返回域名,就将该域名添加进hosts ...

  5. Oracle表的导出、导入

    有些情况下,需要单独导出某些表,用或者分析数据. 下面记录Oracle表的导出导入方法 1. 表的导出 ./exp $username/$passwd@$ORACLE_SID file=/$file_ ...

  6. 【pandas小技巧】--列值的映射

    映射列值是指将一个列中的某些特定值映射为另外一些值,常用于数据清洗和转换. 使用映射列值的场景有很多,以下是几种常见的场景: 将字符串类型的列中的某些值映射为数字.例如,将"男"和 ...

  7. Kali开机启动模式修改

    kali Linux安装之后默认启动图形化界面,为了减轻系统负担,可以修改启动进入字符界面. 具体步骤如下: 1.打开引导配置文件 vim /etc/default/grub 2.修改GRUB_CMD ...

  8. IDA的使用2

    IDA的使用2 string类型的选择 Rename 要注意如果再namelist和public name里面是不能重名 操作数 这个主要和开发结合精密, change sign-改变符号 bitwi ...

  9. Controller 层代码技巧

    Controller 层代码技巧 前言 本篇主要要介绍的就是controller层的处理,一个完整的后端请求由4部分组成: 接口地址(也就是URL地址) 请求方式(一般就是get.post,当然还有p ...

  10. Linux 内核 ASoC DMA 引擎驱动程序

    Linux 内核 ASoC 框架,在概念上将嵌入式音频系统拆分为多个可复用的组件驱动程序,包括 Codec 类驱动程序.平台类驱动程序和机器类驱动程序.在实现上,机器类驱动程序用 struct snd ...