首页 > 编程语言 >并发编程 --- CAS原子操作

并发编程 --- CAS原子操作

时间:2023-08-13 18:46:17浏览次数:36  
标签:无锁 obj val CAS 编程 --- ref Interlocked

介绍

CAS(Compare And Swap) 是一种无锁算法的实现手段,中文名称为比较并交换。它由 CPU 的原子指令实现,可以在多线程环境下实现无锁的数据结构。

原理

CAS 的原理是:它会先比较内存中的某个值是否和预期值相同,如果相同则更新这个值,否则不做任何操作。这整个过程是原子的,所以可以在多线程环境下实现无锁的数据结构。

CAS 操作有3个原子性操作:

  1. 读取内存的值
  2. 将内存的值与期望值比较
  3. 如果相等,则将内存值更新为新值

这三个操作一起完成,中间不会被线程切换打断。这就保证了比较和交换的原子性。

使用C#伪代码实现如下:

bool Cas(ref object obj, object expected, object newValue)
{
    object oldValue = obj;
    if (oldValue == expected) {
        obj = newValue;
        return true;
    }
    return false;
}

解释如下:

  • C#使用ref关键字来表示要操作的内存地址obj
  • oldValue = obj读取内存值,obj = newValue更新内存值。
  • 其他逻辑与伪代码相同,先读取内存值oldValue,然后判断是否等于期望值expected,如果相等则更新内存值为newValue并返回true,否则返回false。
  • CAS操作包含读内存值、比较内存值与期望值、更新内存值三个原子步骤。这三步作为一个整体执行,中间不会被中断,保证比较和交换的原子性。
  • 该方法尝试使用CAS操作更新obj的值,当且仅当obj的值等于expected时才更新,否则不做任何操作。

示例

C# 中提供了 Interlocked 类来实现 CAS 操作。主要包含以下几个方法:

  • Interlocked.CompareExchange(ref val, newValue, comparand):如果 val 等于 comparand,则将 val 的值更新为 newValue,并返回 comparand,否则返回 val 的当前值。
  • Interlocked.Exchange(ref val, newValue):将 val 的值更新为 newValue,并返回 val 的旧值。
  • Interlocked.Increment(ref val):将 val 的值增加 1,并返回增加后的值。
  • Interlocked.Decrement(ref val):将 val 的值减少 1,并返回减少后的值。
    示例代码:
int val = 0;
Interlocked.CompareExchange(ref val, 1, 0); // val = 1, 返回 0  
Interlocked.CompareExchange(ref val, 2, 1); // val 保持 1, 返回 1
Interlocked.Increment(ref val);           // val = 2, 返回 2
Interlocked.Decrement(ref val);           // val = 1, 返回 1

这些方法可以实现无锁数据结构,例如无锁队列:

public class LockFreeQueue<T>
{
    private T[] array;
    private int head;
    private int tail;

    public void Enqueue(T obj)
    {
        int tail = Interlocked.Increment(ref this.tail);
        this.array[tail % this.array.Length] = obj;
    }

    public T Dequeue()
    {
        int head = Interlocked.Increment(ref this.head);
        return this.array[head % this.array.Length];
    }
} 

通过 Interlocked 类的原子操作实现了无锁入队出队,这是一个典型的使用 CAS 实现无锁算法的例子。

CAS优缺点

优点:

  • 无锁,实现高并发的数据结构。CAS 是实现无锁算法的关键手段。
  • 原子操作,线程安全,不会引起数据竞争。
  • 简单高效,只需要硬件支持,性能很高。

缺点:

  • ABA 问题。如果一个值从 A 改为 B,又改回 A,那么 CAS 操作会误认为值没有改变。常用的解决方法是使用版本号。
  • 只能保证一个共享变量的原子操作。如果对多个共享变量操作,则需要使用锁。
  • 资源浪费。当 CAS 失败时,会进行重试,消耗 CPU 资源。
  • 只能在某些平台使用。需要硬件对 CAS 操作的支持,一些低端硬件并不支持 CAS

综上,CAS 是实现无锁算法的关键手段,有很高的性能,但是也存在一定的问题,需要权衡使用。

一般适用场景:

  • 当对一个共享变量的原子操作时,使用 CAS
  • 当操作多个共享变量时,使用锁可能性能更高。
  • 如果硬件不支持 CAS,也不得不使用锁。

结论

CAS 是实现无锁算法的关键手段,性能高并发度高,但是也存在一定问题,需要权衡使用。一般来说,当操作一个共享变量时使用 CAS,操作多个共享变量时使用锁可能更高效。如果硬件不支持 CAS,也只能使用锁。

此外,CAS 和锁是两种不同的同步原语,各有优缺点,需要根据实际情况选择使用。CAS 是无锁算法的基石,所以高性能高并发系统中还是比较重要的

标签:无锁,obj,val,CAS,编程,---,ref,Interlocked
From: https://www.cnblogs.com/pandefu/p/17536261.html

相关文章

  • 小工具 --- 树形展示多属性复杂结构类
    灵感最近在做配置模块,然后整个配置的参数是非常多的,层级结构也很深。可能有几百个参数,三、四层的层级关系,想要捋顺所有的类和参数,太繁琐了,而且VisualStudio的类视图只能看到属性,却看不出层级关系来,所以花费些许精力,写一个控制台小程序,展示类结构。原理就是通过反射得到所有属......
  • nacos安装-win
    Nacos安装指南1.Windows安装开发阶段采用单机安装即可。1.1.下载安装包在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:GitHub主页:https://github.com/alibaba/nacosGitHub的Release下载页:https://github.com/alibaba/nacos/releases如图:1.2.解......
  • 数据结构与算法 --- 组数、链表、栈和队列(一)
    数组、链表、栈和队列是四种基础数据结构,他们是高级、复杂的数据结构和算法的基础。本篇先来讲述数组,链表,及算法的优化策略。数组定义数组:数组是一种线性表数据结构,它用一组连续的内存空间存储一组具有相同类型的数据。定义中有三个关键词:线性表连续的内存空间相同类型数......
  • 数据结构与算法 --- “哨兵”思想
    引言哨兵思想是指在算法中使用一个特殊值来检测或标记某些条件的发生,它的目的是为了简化代码,并使其更容易理解,常常用于在循环中优化边界条件的判断。介绍在算法中,"哨兵"思想是指在循环中设置一个特殊的元素(称为哨兵),以便在循环过程中能够更高效地处理某些边界情况或结束条件。......
  • 数据结构与算法 --- 组数、链表、栈和队列(二)
    继数据结构与算法---组数、链表、栈和队列(一)讲解完数组,链表及算法的优化策略之后,接下来继续讲解两种特殊的线性表结构,栈和队列。栈对“栈”有一个很形象的比喻,栈就像一摞叠在一起的盘子,放盘子时,只能放在上面,不能将盘子插入到中间的任意位置;取盘子时,只能从最上面取,不能从中间任......
  • 解读 --- 深拷贝
    引言深拷贝是指创建一个新对象,该对象的值与原始对象完全相同,但在内存中具有不同的地址。这意味着如果您对原始对象进行更改,则不会影响到复制的对象常见的C#常见的深拷贝方式有以下4类:各种形式的序列化及反序列化。通过反射机制获取该对象的所有字段和属性信息。遍历所有字段......
  • 数据结构与算法 --- 排序算法(一)
    引言按照时间复杂度,将一些常见排序算法进行分类,分为以下三类:\(O(n^2)\):冒泡排序,插入排序,选择排序。\(O(nlogn)\):快速排序,归并排序。\(O(n)\):桶排序,计数排序,基数排序。本篇文章讨论以下第一类:冒泡排序,插入排序,选择排序。上一篇数据结构与算法---如何分析排序算法提......
  • 数据结构与算法 --- 递归(二)
    引言上文数据结构与算法---递归(一)讲述了什么是递归算法,如何编写递归算法及如何写好递归算法,本文着重讲述一下如何避免递归过深导致的堆栈溢出问题。探究产生堆栈溢出的原因函数调用采用函数调用栈来保存当前“快照”(局部变量,返回地址等)。函数调用栈是内存中开辟的一块存储空......
  • 数据结构与算法 --- 递归(一)
    什么是递归?递归(Recursion)是一种解决问题的方法,它将问题分解为更小的子问题,并逐层解决这些子问题。递归算法的核心思想是:一个函数可以直接或间接地调用自身。通过这种自我调用,我们可以用简洁的代码来解决复杂问题。满足递归的条件一般来说,满足下面三个条件就可以使用递归:待......
  • 数据结构与算法 --- 排序算法(二)
    title:数据结构与算法---排序算法(二)category:数据结构与算法tags:算法updatedAt:2023-05-18T15:29:17.847ZcreatedAt:2023-05-13T14:43:31.656Z引言上一篇数据结构与算法---排序算法(一)中,学习了冒泡排序,插入排序,选择排序这三种时间复杂度为\(O(n^2)\)的算法。实......