CAS的小实验

CAS是什么?

cas是一种在多线程的编程中的一种避免竞争态的方法,作用类似与互斥锁。当多个线程对一个共享的数据进行访问的时候,如果多个线程同时对这个数据进行写,而没有采取任何方法避免多线程同时写,数据就会出现各种问题。


解决这个问题的最经典的方法就是使用互斥锁了。一个线程需要对那个数据进行访问之前,就先对那个数据上锁,而互斥锁只能被一个实体(线程)获取,在获取互斥锁的线程释放那个锁之前,其他线程如果尝试获取那个锁,就会陷入休眠。这样子,保证只有一个线程能够操纵那个数据,就不会有问题了。互斥锁属于悲观锁,就是它会假设对数据的操作会出问题,所以作出最坏的心里准备。但是,锁的操作比较费时,这涉及操作系统比较底层的事情,例如,对锁的操作涉及上下文的切换等。


cas属于乐观锁,即对于数据的操纵不会作出太坏的假设。cas的本质是,先获取数据在内存中的一个旧的值oldValue,然后对这个数据进行修改的时候,先用oldValue与当前内存中的数据值newValue进行对比,如果oldValue == newValue,就把想要对这个数据的修改施加到这个数据身上。否则,在重新进行整个cas的过程。


举个例子,当前有多个线程,有一个变量a。多个线程同时对a做a=a+1的操作。假设只有两个线程b和c。先描述一下没有cas的情况下,a的可能的“命运””。(按照事件的发送顺序)

1. b线程取出a的值

        2.b线程计算a+1的值,保存在一个临时变量tmp中

3.c线程取出a的值

4.c线程计算a+1的值,保存在一个临时变量tmp1中

5.b线程将tmp的值赋给a

6.c线程把tmp1的值赋给a


可以发现,其实在步骤5和步骤6之后,a的值是一样的。因为步骤2和4分别在b和c线程中存了一份a+1的副本,然后在步骤5和6中分别将这个副本的值写到a中,步骤5中的操作的结果被覆盖了


这个就是多线程中如果不进行保护措施的时候会发生的问题。那么cas是怎么进行对数据的保护的呢?举同样的例子。下面列出a的“命运”

1.b线程取出a的值,存到oldValue中。

2.b线程计算出oldValue+1的值,存到newValue中。

3.c线程取出a的值,存到oldValue1中。

4.c线程计算oldValue+1的值,存到newValue1。

5.b线程用oldValue与a所在的地址中的值*(&a)进行比较,发现oldValue == *(&a)相等,就把newValue赋给a,即a=newValue。

6.c线程用oldValue1与a所在的地址中的值*(&a)进行比较,发现oldValue != *(&a),因为 *(&a)已经被b线程写了。此时,c线程重新进行整个过程,即先取出a中的值到oldValue2,计算a+1的值存到newValue2,将oldValue2与*(&a)进行比较,发现相等(如果没有其他线程对a进行操作),把newValue2写到a中。


这样,就不会出现写操作被覆盖的问题了。同时,也没有使用了锁。


cas在gcc中有内置的原子操作支持:__sync_val_compare_and_swap和__sync_bool_compare_and_swap,这两个主要是返回值的不同。原子操作就是,将oldValue与a的值进行比较的操作和将newValue写入a的操作合并为“一体”,要么两个操作都完成,要么都没有完成,而不会在进行比较之后a的值被其他线程修改。


下面是一个代码的例子:

#include <cstdio>
#include <pthread.h>
#include <unistd.h>

void* testCAS(void * op)
{
    int *tmp = (int*)op;
    int toTest = *tmp;
    usleep(5);//睡眠,使得*tmp可以被其他线程进行修改
    while(!__sync_bool_compare_and_swap(tmp,toTest,toTest+1))//cas操作
    {
        toTest = *tmp;
        printf("sync fail\n");
    }


    return NULL;
}


int main()
{
    pthread_t threads[10];
    int toTest = 0;
    for(int i=0;i<10;i++)
    {
        pthread_create(&threads[i],NULL,testCAS,&toTest);
    }
    for(int i=0;i<10;i++)
    {
        if(pthread_join(threads[i],NULL)<0)
        {
            printf("pthread_join threads[%d] fail\n",i);
        }
    }
    printf("the final toTest: %d\n",toTest);

}


CAS的应用

其实,基本有多线程的情况下,基本都可以或多或少的使用cas来对数据进行保护。


我最近知道的一个应用场景是使用CAS来实现无锁队列。无锁队列就是多线程对一个队列进行出队和入队的操作。如果队列使用了链表来实现,就会涉及到头指针和尾指针的修改。这两个指针的值需要应用cas来防止多个线程同时进行修改。如果队列使用数组来实现,那么记录队列的起始位置和末尾位置,这个也是需要用cas来进行保护的。前面说到,相对于使用锁,cas速度更快。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值