简单画了一张图 (灵魂画手 →_→)

如图 ConcurrentDictionary 其中有个tables 对象主要存储,而这个 tables 是一个 很多区块的 数组 ,每个区块 又是一个node的链表 (ps: 一个node 就是一个key value 对)

具体实现如下(ps 代码摘自 net4.5):

   private volatile ConcurrentDictionary<TKey, TValue>.Tables m_tables;

 private class Tables
{
internal readonly ConcurrentDictionary<TKey, TValue>.Node[] m_buckets;
internal readonly object[] m_locks;
internal volatile int[] m_countPerLock;
internal readonly IEqualityComparer<TKey> m_comparer; internal Tables(ConcurrentDictionary<TKey, TValue>.Node[] buckets, object[] locks, int[] countPerLock, IEqualityComparer<TKey> comparer)
{
this.m_buckets = buckets;
this.m_locks = locks;
this.m_countPerLock = countPerLock;
this.m_comparer = comparer;
}
} private class Node
{
internal TKey m_key;
internal TValue m_value;
internal volatile ConcurrentDictionary<TKey, TValue>.Node m_next;
internal int m_hashcode; internal Node(TKey key, TValue value, int hashcode, ConcurrentDictionary<TKey, TValue>.Node next)
{
this.m_key = key;
this.m_value = value;
this.m_next = next;
this.m_hashcode = hashcode;
}
}

看 这个Node类是一个带next 指针的结构 ,一个node就是链表  ,而Tables类中 m_buckets 变量便是一个 存储了n个链表的列表结构

其中Tables类中有个变量名为 countPerLock 类型为 int[] 便是图中最下面那个框 这个,这个变量 主要是用来 统计字典中数据的个数 ,这个 countPerLock 与m_buckets 数量 一一对应,一个node对应countPerLock 中的一个元素。Count()这个方法便是主要使用这个元素经行统计。这样的好处是不用遍历node链表。

最重要的是Tables 中m_locks的实现。 这是一个锁的列表 其中用来控制 多线程读取修改时 控制的区块 。

当字典初始化的时候 m_locks 的数量为 cpu内核数*4(ps:例如i7 就是8*4=32) 而  m_buckets 数量初始化是31(有个小条件是m_locks>=m_buckets 时 m_locks=m_buckets) 所以i7 下 m_buckets 和 m_locks 都是32个 ,理论上再,不添加新节点的时候 一个区块对应 一个锁。

m_locks 初始化时是默认值是 cpu内核数*4  最大值是 1024个

m_buckets初始化默认值 31  最大值是2146435071

也就是说 如果字典中数据量大的时候 是一个锁对象 对应n个Node链表。

关于ConcurrentDictionary中所有读取操作

例如Keys Values Count 这类的属性 会对锁定 m_locks 中所有的锁对象 所以需要谨慎使用。

而常用的索引器[]和 TryGetValue 等方法 未锁定任何锁对象,并通过 Volatile.Read 原子性读取 对应的Node链表 遍历中所有元素 直到找到 对应的key 为止。

关于ConcurrentDictionary中所有写操作

当添加一个新数据时 方法会计算key的hashcode  是放到哪个node里表中 然后锁定对应的锁对象,自后 通过 Volatile.Write 方法 替换新的Node的指向 , 这时node为新的值 它的next 指向原先的Node值。

 private void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
{
bucketNo = (hashcode & int.MaxValue) % bucketCount; //Node的位置
lockNo = bucketNo % lockCount; // 锁位置
} Volatile.Write<ConcurrentDictionary<TKey, TValue>.Node>(ref tables.m_buckets[bucketNo], new ConcurrentDictionary<TKey, TValue>.Node(key, value, hashCode, tables.m_buckets[bucketNo]));

为什么这么设计?

对于多线程 这种多个链表 多个锁对象 可以提升 多个线程同时操作的可能性 ,因为很大的程度上写操作的数据  并不是一个锁对象负责的。

同时链式的存储 对于添加对象而言 内存的操作更方便

浅析C#中 ConcurrentDictionary的实现的更多相关文章

  1. 浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  2. 浅析mongodb中group分组

    这篇文章主要介绍了浅析mongodb中group分组的实现方法及示例,非常的简单实用,有需要的小伙伴可以参考下. group做的聚合有些复杂.先选定分组所依据的键,此后MongoDB就会将集合依据选定 ...

  3. 浅析py-faster-rcnn中不同版本caffe的安装及其对应不同版本cudnn的解决方案

    浅析py-faster-rcnn中不同版本caffe的安装及其对应不同版本cudnn的解决方案 本文是截止目前为止最强攻略,按照本文方法基本可以无压力应对caffe和Ross B. Girshick的 ...

  4. 浅析JS中的模块规范(CommonJS,AMD,CMD)////////////////////////zzzzzz

    浅析JS中的模块规范(CommonJS,AMD,CMD)   如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已.     ...

  5. 浅析Java中的访问权限控制

    浅析Java中的访问权限控制 今天我们来一起了解一下Java语言中的访问权限控制.在讨论访问权限控制之前,先来讨论一下为何需要访问权限控制.考虑两个场景: 场景1:工程师A编写了一个类ClassA,但 ...

  6. [转载]浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  7. 浅析 JavaScript 中的 函数 currying 柯里化

    原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...

  8. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  9. 【转】浅析Java中的final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...

随机推荐

  1. python基础之递归,匿名,内置函数

    递归函数: 什么是递归函数? 函数递归调用:在调用一个函数的过程中,又直接或间接地调用了该函数本身. 递归必须要有两个明确的阶段: ①递推:一层一层递归调用下去,强调:每进入下一层问题规模减少 ②回溯 ...

  2. kong结合consul

    早期版本 ./etcdctl ls / --recursive /name /name1 /name1/wyc /name1/wu /name1/chao /name1/chao/age ------ ...

  3. Z-Stack协议栈网络号与信道号的设置

    1.网络号设置 默认是:-DZDAPP_CONFIG_PAN_ID=0xFFFF 代表的是随机选择一个网络号,并加入此网络,可以将0xffff改成4位数字,如图所示. 2.信道号的设置 这个地方在网络 ...

  4. Problem H: 零起点学算法103——查找最大元素

    #include<stdio.h> #include<string.h> int main() { ]; while(gets(a)!=NULL) { ]; ;a[i]!='\ ...

  5. [转]解析UML建模语言中的UML图分类、 UML各种图形及作用

    本文向大家介绍一下UML图分类,作为一种建模语言,UML的定义包括UML语义和UML表示法两个部分. UML图大致可分为五类,共有九种图形. AD: 本文和大家重点讨论一下UML图分类,标准建模语言U ...

  6. [典型漏洞分享]一个典型的XSS盲打漏洞可导致全网用户cookie被盗取

    偶平时在做安全测试时,一般是以发现问题为主,点到为止,但做安全的同学可能也遇到过这样的问题,当你尝试向开发的同学描述一个漏洞危害怎么怎么样的时候,双方经常会有一种鸡同鸭讲的感觉,甚至他们觉得我们在夸大 ...

  7. easyui-combobox绑定回车事件相关

    去掉combobox回车内容不匹配清空输入项     问题描述:easyui的combobox插件,输入的内容如果和选项不匹配时,按下回车会导致输入的内容被清空. 解决办法:要解决回车时,combob ...

  8. Ajax和Jsonp实践

    之前一直使用jQuery的ajax方法,导致自己对浏览器原生的XMLHttpRequest对象不是很熟悉,于是决定自己写下,以下是个人写的deom,发表一下,聊表纪念. Ajax 和 jsonp 的j ...

  9. iOS:在OC中调用JS脚本

    示例一:在webView中调用js脚本进行搜索 1.首先导入JavaScriptCore.framework这个框架 2.创建webView.设置代理.请求手机端百度 #import "Vi ...

  10. Indirect Buffers

    间接cmd 用在 gpu建立的cmd 不需要cpu参与 用indirect cpu gpu就不会互相等待了 直接gpu建立了 gpu用 之前的cmd是cpu建立 填入数据 gpu拿来执行的 在meta ...