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

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

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

  1. /// <summary>
  2. /// 原子操作-计数
  3. /// </summary>
  4. public void Demo1()
  5. {
  6. Task.Run(() =>
  7. {
  8. long total = ;
  9. long result = ;
  10.  
  11. PrintInfo("正在计数");
  12.  
  13. Parallel.For(, , (i) =>
  14. {
  15. for (int j = ; j < ; j++)
  16. {
  17. Interlocked.Increment(ref total);
  18. result++;
  19. }
  20. });
  21.  
  22. PrintInfo($"操作结果应该为\t\t: {10 * 10000000}");
  23. PrintInfo($"原子操作结果\t\t: {total}");
  24. PrintInfo($"i++操作结果\t\t: {result}");
  25. });
  26. }

原子操作-计数

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

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

二、不一样的单例模式

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

  1. /// <summary>
  2. /// 原子操作-单例模式
  3. /// </summary>
  4. public void Demo2()
  5. {
  6. ConcurrentQueue<InterlockedSingleClass> queue = new ConcurrentQueue<Demo.InterlockedSpinLockClass.InterlockedSingleClass>();
  7.  
  8. // 虽然这个测试不严谨、但也或多或少的说明了一些问题
  9. for (int i = ; i < ; i++) // 同时分配的线程数过多、调度器反而调度不过来
  10. {
  11. Task.Run(() =>
  12. {
  13. var result = InterlockedSingleClass.SingleInstance;
  14.  
  15. queue.Enqueue(result);
  16. });
  17. }
  18.  
  19. // 1秒钟后显示结果
  20. Task.Delay().ContinueWith((t) =>
  21. {
  22. PrintInfo($"利用原子操作-单例模式、生成的对象总数:{queue.Count}");
  23.  
  24. InterlockedSingleClass firstItem = null;
  25. queue.TryDequeue(out firstItem);
  26.  
  27. for (int i = ; i < queue.Count;)
  28. {
  29. InterlockedSingleClass temp = null;
  30. queue.TryDequeue(out temp);
  31.  
  32. if (temp == null || firstItem == null || !object.ReferenceEquals(temp, firstItem))
  33. {
  34. PrintInfo("单例模式失效");
  35. }
  36. }
  37.  
  38. PrintInfo("原子操作-单例模式-运行完毕");
  39. });
  40.  
  41. }
  42.  
  43. public class InterlockedSingleClass
  44. {
  45. private static InterlockedSingleClass single = null;
  46.  
  47. public static InterlockedSingleClass SingleInstance
  48. {
  49. get
  50. {
  51. // if (single == null) // 为了测试效果,该行代码注释掉
  52. {
  53. Interlocked.CompareExchange<InterlockedSingleClass>(ref single, new InterlockedSingleClass(), null);
  54. }
  55.  
  56. return single;
  57. }
  58. }
  59.  
  60. }

原子操作-单例模式

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

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

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

三、自旋锁

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

  1. /// <summary>
  2. /// 自旋锁Demo,来源MSDN
  3. /// </summary>
  4. public void Demo3()
  5. {
  6. SpinLock sl = new SpinLock();
  7.  
  8. StringBuilder sb = new StringBuilder();
  9.  
  10. // Action taken by each parallel job.
  11. // Append to the StringBuilder 10000 times, protecting
  12. // access to sb with a SpinLock.
  13. Action action = () =>
  14. {
  15. bool gotLock = false;
  16. for (int i = ; i < ; i++)
  17. {
  18. gotLock = false;
  19. try
  20. {
  21. sl.Enter(ref gotLock);
  22.  
  23. sb.Append((i % ).ToString());
  24. }
  25. finally
  26. {
  27. // Only give up the lock if you actually acquired it
  28. if (gotLock)
  29. sl.Exit();
  30. }
  31. }
  32. };
  33.  
  34. // Invoke 3 concurrent instances of the action above
  35. Parallel.Invoke(action, action, action);
  36.  
  37. // Check/Show the results
  38. PrintInfo($"sb.Length = {sb.Length} (should be 30000)");
  39.  
  40. PrintInfo($"number of occurrences of '5' in sb: {sb.ToString().Where(c => (c == '5')).Count()} (should be 3000)");
  41.  
  42. }

自旋锁

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

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

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

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

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

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

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

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

(未完待续...)

.NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)的更多相关文章

  1. Linux内核中锁机制之原子操作、自旋锁

    很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多 ...

  2. 大话Linux内核中锁机制之原子操作、自旋锁

    转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其 ...

  3. 大话Linux内核中锁机制之原子操作、自旋锁【转】

    转自:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实 ...

  4. Nginx学习之四-Nginx进程同步方式-自旋锁(spinlock)

    自旋锁简介 Nginx框架使用了三种消息传递方式:共享内存.套接字.信号. Nginx主要使用了三种同步方式:原子操作.信号量.文件锁. 基于原子操作,nginx实现了一个自旋锁.自旋锁是一种非睡眠锁 ...

  5. 转:自旋锁(spinlock)

    自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名. 由于 ...

  6. 抢占式内核与非抢占式内核中的自旋锁(spinlock)的差别

    一.概括 (1)自旋锁适用于SMP系统,UP系统用spinlock是作死. (2)保护模式下禁止内核抢占的方法:1.运行终端服务例程时2.运行软中断和tasklet时3.设置本地CPU计数器preem ...

  7. 用户模式构造-简单自旋锁(SpinLock)

    internal sealed class SimpleSpinLock { //0等于false(默认),1等于true ; public void Enter() { while (true) { ...

  8. .NET 同步与异步 之 警惕闭包(十)

    本随笔续接:.NET 同步与异步 之 原子操作和自旋锁(Interlocked.SpinLock)(九) 至此.同步与异步 相关的常规操作(比较常见的操作).差不多已经介绍完毕. 本随笔就着重说一下闭 ...

  9. .NET同步与异步之相关背景知识(六)

    在之前的五篇随笔中,已经介绍了.NET 类库中实现并行的常见方式及其基本用法,当然.这些基本用法远远不能覆盖所有,也只能作为一个引子出现在这里.以下是前五篇随笔的目录: .NET 同步与异步之封装成T ...

随机推荐

  1. Codeforces 739C Alyona and towers 线段树

    Alyona and towers 这个题写起来真的要人命... 我们发现一个区间被加上一个d的时候, 内部的结构是不变的, 改变的只是左端点右端点的值, 这样就能区间合并了. 如果用差分的话会简单一 ...

  2. 【Java】 剑指offer(1) 找出数组中重复的数字

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 在一个长度为n的数组里的所有数字都在0到n-1的范围内.数组中某些数字 ...

  3. 038 关于HIVE的配置

    一:常用版本 1.版本 0.13.1 2014年 1.2.1   2015年 版本之间的区别:支持SQL不同,向关系型数据库靠拢. 目前看到的版本是2.3.2了. 2.官网 二:安装配置hive 1. ...

  4. @RequestParam注解使用:Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.

    详细错误信息 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Re ...

  5. 一段让自己好好理解reduce的代码

    const pick = (obj, arr) => arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[ ...

  6. JavaScript访问对象属性

    在JavaScript中,可以使用“ . ”和“ [ ] ”访问对象的属性. 1.点表示法 使用“ . ”运算符来存取一个对象的属性时,属性名是用标识符表示的.而在JavaScript程序中,标识符必 ...

  7. 一些日常工具集合(C++代码片段)

    一些日常工具集合(C++代码片段) ——工欲善其事,必先利其器 尽管不会松松松,但是至少维持一个比较小的常数还是比较好的 在此之前依然要保证算法的正确性以及代码的可写性 本文依然会持久更新,因为一次写 ...

  8. 潭州课堂25班:Ph201805201 第五课:格式化输出和深浅复制 (课堂笔记)

    格式化输出和字符串转义 占位符 使用示意 作用 %s '%s %s' % ('hello', 'world') 表示占位的是str %d '%d %d' % (1, 2) 表示占位的是int %d ' ...

  9. js签名

    <!doctype html><html lang="en"> <head> <meta charset="UTF-8" ...

  10. 【原创】python模拟腾讯网页登录

    近日,研究Tencent网页模拟登录的过程,过程有些忐忑,但最终还是实现了这一功能.先将结果写于此,供大家参考: 其加密过程在c_login_old.js文件中执行,将JS关键代码提取出来如下: fu ...