CAS原理

作者: 钟离惜 | 来源:发表于2021-01-17 16:52 被阅读0次

一、什么是CAS原子操作

大家应该还记得操作系统里面关于“原子操作”的概念,一个操作是原子的(atomic),假设这个操作所处的层(layer)的更高层不能发现其内部实现与结构。原子操作能够是一个步骤,也能够是多个操作步骤。可是其顺序是不能够被打乱,或者分割掉仅仅运行部分。有了这个原子操作这个保证我们就能够实现无锁了。

CAS原子操作在维基百科中的代码描写叙述例如以下:

int compare_and_swap(int* reg, int oldval, int newval)
{
    ATOMIC();
    int old_reg_val = *reg;
    if (old_reg_val == oldval)
        *reg = newval;
    END_ATOMIC();
    return old_reg_val;
}

也就是检查内存*reg里的值是不是oldval,假设是的话。则对其赋值newval。上面的代码总是返回old_reg_value,调用者假设须要知道是否更新成功还须要做进一步推断,为了方便,它能够变种为直接返回是否更新成功,例如以下:

 bool compare_and_swap (int *accum, int *dest, int newval)
 {
   if ( *accum == *dest ) {
       *dest = newval;
       return true;
   }
   return false;
 }

二、CAS 在各个平台下的实现

2.1 Linux GCC 支持的 CAS
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
2.2 Windows支持的CAS
InterlockedCompareExchange ( __inout LONG volatile *Target,
                                 __in LONG Exchange,
                                 __in LONG Comperand);
2.3C++ 11支持的CAS
template< class T >
bool atomic_compare_exchange_weak( std::atomic<T>* obj,
                                    T* expected, T desired );

三、CAS原子操作实现无锁的性能分析

3.1 测试方法叙述

测试平台是虚拟机下Centos7.4
方法a使用C11的互斥锁mutex,方法b使用Linux的__sync_bool_compare_and_swap,方法c使用C11的compare_exchange_weak
采用控制变量法,每种方法起100个线程控制各自的变量(初始0),保证线程安全,在各自的线程函数中循环10000次加法,然后重复上述操作100次,也就是最后的结果应该都是100*10000*100=1亿。
对比a、b、c三种方法的运行时间。

3.2 测试代码如下(计算时间类在后面给出):
#include <cstdio>
#include <mutex>          // std::mutex
#include <atomic>         // std::atomic
#include <thread>         // std::thread
using namespace std;

#include "timer.h"

#define TIMES (100)
#define THREDS (100)
#define LOOPNUM (10000)

mutex mm;
size_t a = 0;
void funa()
{
    for (int i = 0; i < LOOPNUM; i++)
    {
        mm.lock();
        a++;
        mm.unlock();
    }
}
size_t b = 0;
void funb()
{
    for (int i = 0; i < LOOPNUM; i++)
    {
        size_t oldb = b;
        while (!__sync_bool_compare_and_swap(&b, oldb, oldb + 1))
        {
            oldb = b;
        }
    }
}
std::atomic<size_t> c(0);
void func()
{
    for (int i = 0; i < LOOPNUM; i++)
    {
        size_t oldc = c;
        while (!c.compare_exchange_weak(oldc, oldc + 1))
        {
            oldc = c;
        }
    }
}

int main(int argc, const char *argv[])
{
    CUseTime totalTime;
    {
        CUseTime useTime;
        for (int j = 0; j < TIMES; ++j)
        {
            thread* ttb[THREDS];
            for (int i = 0; i < THREDS; i++)
            {
                ttb[i] = new thread(funa);
            }
            for (int i = 0; i < THREDS; i++)
            {
                ttb[i]->join();
                delete ttb[i];
                ttb[i] = NULL;
            }
        }
        printf("use time %ld ms: C11 \"mutex\" a=%d\n", useTime.GetUseTime(), (int)a);
    }
    {
        CUseTime useTime;
        for (int j = 0; j < TIMES; ++j)
        {
            thread* ttb[THREDS];
            for (int i = 0; i < THREDS; i++)
            {
                ttb[i] = new thread(funb);
            }
            for (int i = 0; i < THREDS; i++)
            {
                ttb[i]->join();
                delete ttb[i];
                ttb[i] = NULL;
            }
        }
        printf("use time %ld ms: linux \"__sync_bool_compare_and_swap\" b=%d\n", useTime.GetUseTime(), (int)b);
    }
    {
        CUseTime useTime;
        for (int j = 0; j < TIMES; ++j)
        {
            thread* ttb[THREDS];
            for (int i = 0; i < THREDS; i++)
            {
                ttb[i] = new thread(func);
            }
            for (int i = 0; i < THREDS; i++)
            {
                ttb[i]->join();
                delete ttb[i];
                ttb[i] = NULL;
            }
        }
        printf("use time %ld ms: C11 \"compare_exchange_weak\" c=%d\n", useTime.GetUseTime(), (int)c);
    }
    printf("use time %ld ms total\n", totalTime.GetUseTime());
    
    return 0;
}
3.3 测试结果

为了方便对比对文字做了后处理

[zlm@localhost Debug]$ ./test.out 
use time 23360 ms: a=100000000 C++11 "mutex"
use time  8850 ms: b=100000000 linux "__sync_bool_compare_and_swap"
use time 24190 ms: c=100000000 C++11 "compare_exchange_weak"
use time 56400 ms: total
[zlm@localhost Debug]$ ./test.out 
use time 24950 ms: a=100000000 C++11 "mutex"
use time  8900 ms: b=100000000 linux "__sync_bool_compare_and_swap"
use time 24400 ms: c=100000000 C++11 "compare_exchange_weak"
use time 58250 ms: total

从耗时上可以看出,Linux的__sync_bool_compare_and_swap应该是效果最好的,在保证了线程安全的前提下拥有最少的耗时,几乎是另外两种方式的三分之一。

3.4 消耗时间类头文件

文件名:timer.h
代码如下:

#ifndef _TIMER_
#define _TIMER_

#include <ctime>

class CUseTime
{
public:
    CUseTime() : m_start_time(clock()) {}
    virtual ~CUseTime() {}
    long GetUseTime() { return (clock() - m_start_time) / (CLOCKS_PER_SEC / 1000); };
private:
    long m_start_time;
};

#endif // !_TIMER_

参考文章
CAS原子操作实现无锁及性能分析

相关文章

  • 原理剖析(第 004 篇)CAS工作原理分析

    原理剖析(第 004 篇)CAS工作原理分析 一、大致介绍 二、原理分析 2.1 何为CAS? 2.2 CAS原理...

  • AtomicXXX以及LongAdder底层原理

    要搞懂AtomicXXX等的原理,首先就要了解CAS的原理。 1.CAS 1.1 什么是 CAS? CAS(Com...

  • AQS原理及CAS

    AQS原理 CAS原理

  • 并发编程三:锁

    一、CAS 1.CAS原理 CAS全称为Compare And Swap,比较与交换。CAS是原子性操作的一种实现...

  • 原子操作CAS

    原子操作CAS 1、CAS的基本原理 利用了现代处理器都支持的CAS的指令,循环这个指令,直到成功为止 CAS(C...

  • java多线程-2-Thread相关

    Thread和Runnable 死锁举例 CAS CAS的底层原理:借助C调用CPU指令,lock + cmpxc...

  • CAS原理

    简介 CAS全称为Compare And Set,即比较交换,包含了3个操作数:需要读写的内存位置V(通过位置读出...

  • CAS原理

    1、什么是CAS? CAS:Compare and Swap,即比较再交换。 jdk5增加了并发包java.uti...

  • CAS原理

    1、什么是CAS? CAS:Compare and Swap,即比较再交换。 jdk5增加了并发包java.uti...

  • CAS原理

    CAS的定义 JDK 1.5的时候,Java支持了Atomic类,这些类的操作都属于原子操作; 帮助最大限度地减少...

网友评论

      本文标题:CAS原理

      本文链接:https://www.haomeiwen.com/subject/wrrwaktx.html