当前位置: 首页 > news >正文

.NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)

本随笔续接:.NET 同步与异步之锁(ReaderWriterLockSlim)(八)

 

之前的随笔已经说过、加锁虽然能很好的解决竞争条件,但也带来了负面影响:性能方面的负面影响。那有没有更好的解决方案呢?有,原子操作、即 Interlocked 这个类。

 

一、让我们先看一个计数的原子操作Demo

        /// <summary>
        /// 原子操作-计数
        /// </summary>
        public void Demo1()
        {
            Task.Run(() =>
            {
                long total = 0;
                long result = 0;

                PrintInfo("正在计数");

                Parallel.For(0, 10, (i) =>
                {
                    for (int j = 0; j < 10000000; j++)
                    {
                        Interlocked.Increment(ref total);
                        result++;
                    }
                });

                PrintInfo($"操作结果应该为\t\t: {10 * 10000000}");
                PrintInfo($"原子操作结果\t\t: {total}");
                PrintInfo($"i++操作结果\t\t: {result}");
            });
        }
原子操作-计数

由上述Demo可知、Interlocked 可以很好的保证 64位整型值的计数操作 能否符合预期,而普通的i++操作却出现了竞争条件。

Interlocked 对于整形操作提供的方法还是很多的,这里不多介绍了。

 

二、不一样的单例模式

Interlocked 中提供了 Interlocked.CompareExchange<T> 方法的泛型版本,让我们来看一下,这个泛型版本的一种巧妙的用法。

        /// <summary>
        /// 原子操作-单例模式
        /// </summary>
        public void Demo2()
        {
            ConcurrentQueue<InterlockedSingleClass> queue = new ConcurrentQueue<Demo.InterlockedSpinLockClass.InterlockedSingleClass>();

            // 虽然这个测试不严谨、但也或多或少的说明了一些问题
            for (int i = 0; i < 10; i++) // 同时分配的线程数过多、调度器反而调度不过来
            {
                Task.Run(() =>
                {
                    var result = InterlockedSingleClass.SingleInstance;

                    queue.Enqueue(result);
                });
            }


            // 1秒钟后显示结果
            Task.Delay(1000).ContinueWith((t) =>
            {
                PrintInfo($"利用原子操作-单例模式、生成的对象总数:{queue.Count}");

                InterlockedSingleClass firstItem = null;
                queue.TryDequeue(out firstItem);

                for (int i = 0; i < queue.Count;)
                {
                    InterlockedSingleClass temp = null;
                    queue.TryDequeue(out temp);

                    if (temp == null || firstItem == null || !object.ReferenceEquals(temp, firstItem))
                    {
                        PrintInfo("单例模式失效");
                    }
                }

                PrintInfo("原子操作-单例模式-运行完毕");
            });

        }


        public class InterlockedSingleClass
        {
            private static InterlockedSingleClass single = null;

            public static InterlockedSingleClass SingleInstance
            {
                get
                {
                    // if (single == null) // 为了测试效果,该行代码注释掉
                    {
                        Interlocked.CompareExchange<InterlockedSingleClass>(ref single, new InterlockedSingleClass(), null);
                    }

                    return single;
                }
            }

        }
原子操作-单例模式

针对Interlocked.CompareExchange<T>方法、我介绍两句:

1、第一个参数为 ref 参数,如果第一个参数 和 第三个参数的引用相等,则用第二个参数替换第一个参数的值,并将第一个参数的原始值返回。

2、该泛型方法 只接受类类型的参数。

 

三、自旋锁

自旋锁:提供一个相互排斥锁基元,在该基元中,尝试获取锁的线程将在重复检查的循环中等待,直至该锁变为可用为止。

        /// <summary>
        /// 自旋锁Demo,来源MSDN
        /// </summary>
        public void Demo3()
        {
            SpinLock sl = new SpinLock();

            StringBuilder sb = new StringBuilder();

            // Action taken by each parallel job.
            // Append to the StringBuilder 10000 times, protecting
            // access to sb with a SpinLock.
            Action action = () =>
            {
                bool gotLock = false;
                for (int i = 0; i < 10000; i++)
                {
                    gotLock = false;
                    try
                    {
                        sl.Enter(ref gotLock);

                        sb.Append((i % 10).ToString());
                    }
                    finally
                    {
                        // Only give up the lock if you actually acquired it
                        if (gotLock)
                            sl.Exit();
                    }
                }
            };

            // Invoke 3 concurrent instances of the action above
            Parallel.Invoke(action, action, action);

            // Check/Show the results
            PrintInfo($"sb.Length = {sb.Length} (should be 30000)");

            PrintInfo($"number of occurrences of '5' in sb: {sb.ToString().Where(c => (c == '5')).Count()} (should be 3000)");

        }
自旋锁

 

看完了Demo,让我们再来深入了解一下自旋锁:

1、自旋锁本身是一个结构、而不是类,这样使用过多的锁时不会造成GC压力。

2、自旋锁是以一种循环等待的方式去尝试获取锁,也就是说、在等待期间 会一直占用CPU、如果等待时间过长会造成CPU浪费,而 Monitor会休眠(Sleep)。

3、自旋锁的使用准则:让临界区尽可能短(时间短)、非阻塞的方式。(因为等待时间过长会造成CPU浪费)

4、由于自旋锁是循环等待的方式、在执行方式上和Monitor的休眠不一样,自旋锁的执行速度会更快。而Monitor的休眠方式会造成额外的系统开销,执行速度反而会降低。

 

 

随笔暂告一段落、下一篇随笔按之前的目录顺序应该是介绍WaitHandler家族的, 笔者临时想变更下顺序、下一遍随笔:并发中的闭包。

附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

参见更多:随笔导读:同步与异步


(未完待续...)

 

转载于:https://www.cnblogs.com/08shiyan/p/6479600.html

相关文章:

  • 让mysql查询强制走索引
  • Unity几个有用的游戏运动特效
  • 终端搜索工具
  • ubuntu 15.04
  • STM32 IAP docs
  • Dockerfile构建LNMP分离环境部署wordpress
  • 无人便利店代理的系统用于其他行业是否可以
  • bat遍历目录
  • JGit
  • 1006 等差数列
  • ambari HDFS-HA 回滚
  • V-4-1 vCenter的安装之配置ODBC
  • robotium之does not have a signature matching问题
  • 内核内存碎片管理
  • 第 2 章 Sniffer
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • bootstrap创建登录注册页面
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • JS实现简单的MVC模式开发小游戏
  • node和express搭建代理服务器(源码)
  • php面试题 汇集2
  • spring security oauth2 password授权模式
  • Vue 重置组件到初始状态
  • Yeoman_Bower_Grunt
  • 分布式任务队列Celery
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 你不可错过的前端面试题(一)
  • 如何解决微信端直接跳WAP端
  • 我的面试准备过程--容器(更新中)
  • 优化 Vue 项目编译文件大小
  • 06-01 点餐小程序前台界面搭建
  • MPAndroidChart 教程:Y轴 YAxis
  • ​ubuntu下安装kvm虚拟机
  • #pragam once 和 #ifndef 预编译头
  • (2)MFC+openGL单文档框架glFrame
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (南京观海微电子)——COF介绍
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)JAVA中的堆栈
  • (转)jQuery 基础
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .form文件_SSM框架文件上传篇
  • .NET Core 项目指定SDK版本
  • .NET MVC第三章、三种传值方式
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .Net的C#语言取月份数值对应的MonthName值
  • /etc/shadow字段详解
  • /var/lib/dpkg/lock 锁定问题
  • @vue/cli脚手架
  • [ CTF ] WriteUp-2022年春秋杯网络安全联赛-冬季赛