Rx可以在3.5里用,他带给了3.5可以使用的支持并发的集合类型,说白了,就是提供了Thread-Safe的Collection。

在.NET 4.0之前,是不直接支持线程安全的集合的。除非自己去做lock。而Lock带给我们的除了风险之外,系统的性能下降问题特别明显。在我之前的项目中,需要用到一个Thread-safe的Queue,我当时使用Compare-And-Swap方式实现的。但是这种方式并不能保证高效,个人能力有限,Google也没帮上多大忙。所以.net 4.0一出来的时候,宣布支持ConcurrentQueue<T>,我立刻就抄起Reflector去看看它的实现方式。

当然,除了ConcurrentQueue<T>外,还有其他几个支持线程安全的并发集合类型,看完之后,才发现他们并不全都是Lock-free的,这点还是跟我的判断不同的。

我们常说的Lock-free,对于.net来讲,其实是说就是不用lock(),也就是Monitor这个类。在Team的技术分享中我多次建议大家不要直接写lock,而是写Monitor.TryEnter(object, 200)这样的方式,这样的好处是避免整个程序面临尴尬的状况,毕竟对于一个互联网来说,绝大多数应用并不是追求完整性的,牺牲整个站点的代价太高了。在.net里,我们可以通过memory barrier或者使用CPU的CAS(就是上面说的Compare-And-Swap,那个Interlocked类就是)指令来进行一些粒度锁,这些都是很轻量级的,我们认为他是不会损坏系统性能的。

注:(感谢@Kavin Yang的提醒)

使用lock()的时候,.net会一直等待能够lock才行。而lock()的实现就是调用Monitor.Enter。但是Monitor还有一个叫做TryEnter的方法,参数除了要lock的object外,还提供一个时间参数作为该操作的超时时间。

可以参见:http://msdn.microsoft.com/en-us/library/42h9d380(v=VS.80).aspx

那看几个实现:

ConcurrentQueue<T>和ConcurrentStack<T>

这两个是使用CAS的方式来完成任务的,实现原理是一样的:

   1: private void PushCore(Node<T> head, Node<T> tail)

<!--CRLF-->

   2: {

<!--CRLF-->

   3:     SpinWait wait = new SpinWait();

<!--CRLF-->

   4:     do

<!--CRLF-->

   5:     {

<!--CRLF-->

   6:         wait.SpinOnce();

<!--CRLF-->

   7:         tail.m_next = this.m_head;

<!--CRLF-->

   8:     }

<!--CRLF-->

   9:     while (Interlocked.CompareExchange<Node<T>>(ref this.m_head, head, tail.m_next) != tail.m_next);

<!--CRLF-->

  10: }

<!--CRLF-->

注意到do…while。整个实现都没有使用lock的地方,唯独在CAS失败的时候使用SpinWait的SpinOnce方法。

ConcurrentDictionary<TKey, TValue>

这个类也在mscorlib.dll里,同样位于System.Collections.Concurrent命名空间下。但是它的add、update等方法都是使用了lock来完成的(当然不是完全依赖lock,所以有性能提升)

   1: try

<!--CRLF-->

   2:    {

<!--CRLF-->

   3:        if (acquireLock)

<!--CRLF-->

   4:        {

<!--CRLF-->

   5:            Monitor.Enter(this.m_locks[num3], ref lockTaken);

<!--CRLF-->

   6:        }

<!--CRLF-->

   7:        if (nodeArray != this.m_buckets)

<!--CRLF-->

   8:        {

<!--CRLF-->

   9:            goto Label_000D;

<!--CRLF-->

  10:        }

<!--CRLF-->

  11:        Node<TKey, TValue> node = null;

<!--CRLF-->

  12: ....

<!--CRLF-->

但是对于他的读操作:

   1: public bool TryGetValue(TKey key, out TValue value)

<!--CRLF-->

   2: {

<!--CRLF-->

   3:     int num;

<!--CRLF-->

   4:     int num2;

<!--CRLF-->

   5:     if (key == null)

<!--CRLF-->

   6:     {

<!--CRLF-->

   7:         throw new ArgumentNullException("key");

<!--CRLF-->

   8:     }

<!--CRLF-->

   9:     Node<TKey, TValue>[] buckets = this.m_buckets;

<!--CRLF-->

  10:     this.GetBucketAndLockNo(this.m_comparer.GetHashCode(key), out num, out num2, buckets.Length);

<!--CRLF-->

  11:     Node<TKey, TValue> next = buckets[num];

<!--CRLF-->

  12:     Thread.MemoryBarrier();

<!--CRLF-->

  13:     while (next != null)

<!--CRLF-->

  14:     {

<!--CRLF-->

  15:         if (this.m_comparer.Equals(next.m_key, key))

<!--CRLF-->

  16:         {

<!--CRLF-->

  17:             value = next.m_value;

<!--CRLF-->

  18:             return true;

<!--CRLF-->

  19:         }

<!--CRLF-->

  20:         next = next.m_next;

<!--CRLF-->

  21:     }

<!--CRLF-->

  22:     value = default(TValue);

<!--CRLF-->

  23:     return false;

<!--CRLF-->

  24: }

<!--CRLF-->

能看到这里的处理方式还是很巧妙的,Dictionary这个数据结构本身就是一个设计为读多的类型,read的次数肯定要远远超过write的次数。所以这个ConcrruentDictionary的设计并不是追求lock-free的改/写操作,而是将read操作做到了lock-free!

ConcrruntBat<T>

这个类设计的更有意思,思路很像是采用了Pool的方式(Pool一般有3种常见的使用方式),将pool对应到应用的thread上,他们之间就没有锁的我问题了(或者无损耗的锁),比如说Add操作:

   1: public void Add(T item)

<!--CRLF-->

   2: {

<!--CRLF-->

   3:     ThreadLocalList<T> threadList = this.GetThreadList(true);

<!--CRLF-->

   4:     this.AddInternal(threadList, item);

<!--CRLF-->

   5: }

<!--CRLF-->

   1: private void AddInternal(ThreadLocalList<T> list, T item)

<!--CRLF-->

   2: {

<!--CRLF-->

   3:     bool taken = false;

<!--CRLF-->

   4:     try

<!--CRLF-->

   5:     {

<!--CRLF-->

   6:         Interlocked.Exchange(ref list.m_currentOp, 1);

<!--CRLF-->

   7:         if ((list.Count < 2) || this.m_needSync)

<!--CRLF-->

   8:         {

<!--CRLF-->

   9:             list.m_currentOp = 0;

<!--CRLF-->

  10:             Monitor2.Enter(list, ref taken);

<!--CRLF-->

  11:         }

<!--CRLF-->

  12:         list.Add(item, taken);

<!--CRLF-->

  13:     }

<!--CRLF-->

  14:     finally

<!--CRLF-->

  15:     {

<!--CRLF-->

  16:         list.m_currentOp = 0;

<!--CRLF-->

  17:         if (taken)

<!--CRLF-->

  18:         {

<!--CRLF-->

  19:             Monitor.Exit(list);

<!--CRLF-->

  20:         }

<!--CRLF-->

  21:     }

<!--CRLF-->

  22: }

<!--CRLF-->

Rx系列:

Rx:1-Observable

Rx:2-Observable more

Rx:3-System.CoreEx.dll

Rx:4-[编外篇] .NET4里的Concurrent Collections相关推荐

  1. 解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)

    解剖SQLSERVER 第四篇  OrcaMDF里对dates类型数据的解析(译) http://improve.dk/parsing-dates-in-orcamdf/ 在SQLSERVER里面有几 ...

  2. 「收藏」关于机器学习的知识点,全在这篇文章里了

    尊重原创版权: https://www.qingtianxiaoshuo.com/hot/44432.html 更多内容参考: https://www.qingtianxiaoshuo.com/ 「收 ...

  3. 分享一篇 Science 里不同批次的单细胞数据整合及批次校正方法

    分享一篇 Science 里不同批次的单细胞数据整合及批次校正方法 [1] Zheng L, Qin S, Si W, Wang A, Xing B, Gao R, Ren X, Wang L, Wu ...

  4. 篇百度前员工发表的博客,在这篇长文里回忆了他离开百度的原因、他眼中的百度乱

    这是一篇百度前员工发表的博客,在这篇长文里回忆了他离开百度的原因.他眼中的百度乱象以及对百度文化的反思.全文转载,无删减.          1.离开          离开百度已经一年多了.     ...

  5. java 中间容器 表格_【JAVA SE基础篇】45.迭代器、Collections工具类以及使用容器存储表格...

    本文将要为您介绍的是[JAVA SE基础篇]45.迭代器.Collections工具类以及使用容器存储表格,具体完成步骤: 1.迭代器 迭代器为我们提供了统一遍历容器(List/Map/Set)的方式 ...

  6. 看了两篇园子里的文章,初步懂了点接口的涵义

    之前对于"接口"概念很模糊,今天看了园子里两篇文章,有点开始懂了: http://www.cnblogs.com/AndyFish/archive/2008/08/11/12652 ...

  7. 关于机器学习的知识点,全在这篇文章里了

    导读:作者用超过1.2万字的篇幅,总结了自己学习机器学习过程中遇到知识点."入门后,才知道机器学习的魅力与可怕."希望正在阅读本文的你,也能在机器学习上学有所成. 本文为「大数据」 ...

  8. 关于Java字符串的全部,就在这篇文章里了

    String 可以说是 Java 中最常见的数据类型,用来表示一串文本,它的使用频率非常高,为了小伙伴们着想,我怒肝了一周,把字符串能写的全都写了出来. 来看一下脑图吧,感受一下这份手册涉及到的知识点 ...

  9. JVM性能调优篇07-阿里巴巴Arthas工具详解

    Arthas工具 Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具.支持 JDK6+, 采用命令行交互模式,可以方便的定位和诊断线上程序运行问题.Arthas 官方 ...

最新文章

  1. ThreadLocal怎么实现线程隔离的?可见性问题?为什么要重新定义一个threadLocalHashCode?为什么有内存泄露?弱引用又是什么?
  2. No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386).
  3. telnet入侵linux,教你入侵RedHatLinux
  4. 三星调侃iPhone13苍岭绿配色:受宠若惊
  5. jmeter性能测试之录制脚本
  6. 关键词分词工具_运用表格宏对阿里国际站关键词库进行分组
  7. webstrom 汉化方法
  8. 显式积分,隐式积分和弹簧质点系统(详细公式推导和太极源码)
  9. 论基因检测的必要性,主动把握健康。
  10. 笔记本光驱接口怎么外接台式机硬盘?
  11. 计算机加硬盘后速度变慢,硬盘速度变慢怎么办 硬盘速度慢解决方法步骤介绍【详解】...
  12. Mysql 窗口函数
  13. C++动态库dll生成及调用总结
  14. 顺序栈的基本操作(C++实现)
  15. Linux系统下安装Adobe Flash Player插件的方法
  16. 50个安卓开发者应该熟悉的Android Studio技巧和资源
  17. 浅谈Appium之AppUI自动化
  18. conda搜索安装包时显示没有匹配No match found for: fastaqc. Search: *fastaqc* PackagesNotFoundError:
  19. 使用 AppFuse 的七个理由(中英文两版)
  20. n阶矩阵乘以n阶矩阵的朴素做法

热门文章

  1. 信息学奥赛一本通(1251:仙岛求药)
  2. 暑期训练日志----2018.8.23
  3. 统计数字(信息学奥赛一本通-T1239)
  4. 母牛的故事(HDU-2018)
  5. 信息学奥赛一本通C++语言——1097: 画矩形
  6. 信息学奥赛C++语言: 队伍调整
  7. MySQL主从失败:slave_IO_Running为No
  8. CMake使用介绍(2)
  9. 深度残差收缩网络:(一)背景知识
  10. [Unity] FlowCanvas 使用注意事项