• 原子操作-Interlocked(CAS算法实现)


    乐观锁与悲观锁:

     众所周知锁有两种:乐观锁与悲观锁。独占锁是一种悲观锁,而 Lock 就是一种独占锁,Lock 会导致其它所有未持有锁的线程阻塞,而等待持有锁的线程释放锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止,这个过程叫自旋。而乐观锁用到的机制就是CAS。

    CAS(Compare And Set):比较和交换

    CAS在硬件层面提升效率,多个语句原子操作不可分割。在intel的CPU中,使用cmpxchg指令。

    CAS,是“CompareAnd Swap”的缩写,中文简称就是“比较并替换”,在CAS算法中,它使用了3个基本操作数:内存地址对应的值V,旧的预期值A(旧值),要修改的新值B(新值),当且仅当预期值A和内存值V相同时,才将内存值修改为B,否则什么都不做,最后返回现在的V值。

    CAS在.NET中的实现类是Interlocked,内部提供很多原子操作的方法,最终都是调用Interlocked.CompareExchange()

    说到线程安全,不要一下子就想到加锁,尤其是可能会调用频繁或者是要求高性能的场合。 

    CAS的适用场景:

    读多写少:如果有大量的写操作,CPU开销可能会过大,因为冲突失败后会不断重试(自旋),这个过程中会消耗CPU

    单个变量原子操作:CAS机制所保证的只是一个变量的原子操作,而不能保证整个代码块的原子性,比如需要保证三个变量共同进行原子性的更新,就不得不使用悲观锁了

    Interlocked:

    MSDN 描述:为多个线程共享的变量提供原子操作。主要函数如下:

    Interlocked.Increment    原子操作,递增指定变量的值并存储结果。

    Interlocked.Decrement       原子操作,递减指定变量的值并存储结果。

    Interlocked.Add        原子操作,添加两个整数并用两者的和替换第一个整数

    Interlocked.Exchange原子操作,赋值 

    Interlocked.CompareExchange(ref a, b, c);  原子操作,a参数和c参数比较,  相等b替换a,不相等不替换。方法返回值始终是第一个参数的原值,也就是内存里的值

    使用CompareExchange方法实现Increment功能:

            public static void Increment()
            {
                int init = 0, result = 0;
                do
                {
                    init = count;
                    result = init + 1;
                }
    
                //当init == count时, count=result;当init !=count时, count不变化;
                while (init != Interlocked.CompareExchange(ref count, result, init));
            }

    测试:

         static int count = 0;
            static void Main(string[] args)
            {
                Action action = () =>
                {
                    for (int i = 0; i < 50000; i++)
                    {
                        //count++;//线程不安全,结果小于100000
                        //Interlocked.Increment(ref count);//线程安全,结果等于100000
                        Increment();//线程安全,结果等于100000
                    }
                };
                var t1 = Task.Run(action);
                var t2 = Task.Run(action);
                Task.WaitAll(t1, t2);
                Console.WriteLine(count);
                Console.ReadKey();
            }

    参考:

    https://www.jianshu.com/p/db5c964a61ee

  • 相关阅读:
    MongoDB repair on Ubuntu
    java后台图形相关代码,weblogic报错
    weblogic配置达梦数据源
    详解JavaScript中的this
    web app指南之构建html5离线应用
    android中的跨进程通信的实现(一)——远程调用过程和aidl
    android应用开发全程实录出版
    android窗口管理框架解析
    BizTalk调用SAP系统RFC含多个参数以及DateTime类型参数
    plsql连接64位oracle在windows 764下连接设置方法
  • 原文地址:https://www.cnblogs.com/fanfan-90/p/12996535.html
Copyright © 2020-2023  润新知