CAS(compare and swap)

首先什么是原子操作?

原子本意是“不能被进一步分割的最小粒子”,而原子操作意为”不可被中断的一个或一系列操作”;

处理器如何实现原子操作?

  1. 首先处理器会自动保证基本的内存操作的原子性:处理器保证从系统内存当中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址。
  2. 总线锁保证原子性:所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占使用共享内存。
  3. 缓存锁定保证原子性:锁住总线的消耗太大,于是有了缓存锁,在Lock期间处理器缓存的内存区域会被锁定,其他处理其无法更改该块内存中的数据
CAS(compare and swap),比较和交换,是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。 该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值
现代的大多数CPU都实现了CAS,它是一种==无锁==(lock-free),且==非阻塞==的一种算法,保持数据的一致性

CAS的思想很简单:三个参数,一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false。

cas步骤还原:

  • 需要传递三个参数 1.当前线程中获取的旧值 2.新值 3.内存地址中的值
  • 循环比较旧值和内存地址中的值,直到比较成功为止,即使失败,旧值是用volatile修饰的,保证一旦发生改变,能够被其他线程所察觉.然后再进行比较.
CAS存在一个很明显的问题,即ABA问题。如果变量V初次读取的时候是A,并且在准备赋值的时候检查到它仍然是A,那能说明它的值没有被其他线程修改过了吗?如果在这段期间它的值曾经被改成了B,然后又改回A,那CAS操作就会误认为它从来没有被修改过
代码示例

Exchange:

int exchange(std::atomic<int>& i, int j) 
{
    int old_value(0);
    int new_value(0);

    do
    {
        old_value = i;
        new_value = j;
    } 
    while (!i.compare_exchange_strong(old_value, new_value));

    return new_value;
}

上面的函数是用CAS实现的,没有使用atomic的fetch_and_add,exchange。

那么CAS是怎么保证数据的正确的?注意上面的old_value,old_value中保存着i的副本,在i执行CAS时,如果old_value中的值于i相同,即代表i没有被其他线程修改,保持原值,随后CAS操作成功,i替换为new_value 。如果old_value与i不相等,即代表其他线程修改了i,随后CAS操作失败,重新刷新old_value,new_value 并继续循环。注意这里的重新刷新是为了保证new_value的数值正确,new_value的是基于old_value计算得到,如果old_value与i不相等,即代表new_value的数值是错误的,需要重新刷新然后再去争抢。

上一篇:linux 的交换分区swap


下一篇:Go 语言指针作为函数参数