浅析C#中 ConcurrentDictionary的实现
简单画了一张图 (灵魂画手 →_→)
如图 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的实现的更多相关文章
- 浅析Java中的final关键字
浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...
- 浅析mongodb中group分组
这篇文章主要介绍了浅析mongodb中group分组的实现方法及示例,非常的简单实用,有需要的小伙伴可以参考下. group做的聚合有些复杂.先选定分组所依据的键,此后MongoDB就会将集合依据选定 ...
- 浅析py-faster-rcnn中不同版本caffe的安装及其对应不同版本cudnn的解决方案
浅析py-faster-rcnn中不同版本caffe的安装及其对应不同版本cudnn的解决方案 本文是截止目前为止最强攻略,按照本文方法基本可以无压力应对caffe和Ross B. Girshick的 ...
- 浅析JS中的模块规范(CommonJS,AMD,CMD)////////////////////////zzzzzz
浅析JS中的模块规范(CommonJS,AMD,CMD) 如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已. ...
- 浅析Java中的访问权限控制
浅析Java中的访问权限控制 今天我们来一起了解一下Java语言中的访问权限控制.在讨论访问权限控制之前,先来讨论一下为何需要访问权限控制.考虑两个场景: 场景1:工程师A编写了一个类ClassA,但 ...
- [转载]浅析Java中的final关键字
浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...
- 浅析 JavaScript 中的 函数 currying 柯里化
原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...
- 浅析 JavaScript 中的 函数 uncurrying 反柯里化
柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...
- 【转】浅析Java中的final关键字
谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...
随机推荐
- 【BFS】bzoj2252 [2010Beijing wc]矩阵距离
要注意一开始将所有为'1'的点入队,然后通过一次BFS去更新所有点的距离,直到无法更新为止. #include<cstdio> #include<queue> #include ...
- leetcode 564. Find the Closest Palindrome
leetcode564题目地址 Given an integer n, find the closest integer (not including itself), which is a pali ...
- [典型漏洞分享]上传导致的应用层DOS攻击
YS 视频封面设置功能可上传大量图片,可进行资源消耗型DOS攻击[中] 问题描述: YS允许用户为设备设置封面,后台在处理时允许用户间接可控上传图片的二级路径以及直接可控保存图片的文 ...
- linux之rootfs (UBIFS)
转:http://www.360doc.com/content/11/1208/15/3700464_170655736.shtml 大大小小事情一堆,好久不更新了,这次记录下移植ubifs文件系统步 ...
- 常见Linux命令(非文件操作)
可见 http://man.linuxde.net/systemctl 非常详尽
- CDK安装
1.下载cdk https://developers.redhat.com/products/cdk/download/ 2.在mac OS上更名并加入目录 $ mkdir -p ~/bin $ cp ...
- React个人学习笔记
元素渲染 通过 ReactDOM.render() 方法渲染页面, 可以使用 ES6 class 来定义一个组件: 如何解析HTMl里面的空格: 1. 使用空格的 unicod 编码 : \u0020 ...
- 对象的当前状态使该操作无效 说明: 执行当前 Web 请求期间,出现未处理的异常。
这个异常在页面数据量小的时候并不会触发,只在页面数据量大的情况下才会出现, 异常信息如下: 对象的当前状态使该操作无效. 说明: 执行当前 Web 请求期间,出现未处理的异常.请检查堆栈跟踪信息,以 ...
- iOS:iOS开发系列–打造自己的“美图秀秀”(中)
来源: KenshinCui 链接:http://www.cnblogs.com/kenshincui/p/3959951.html 其他状态设置 常用的图形上下文状态设置上面基本都用到了,我们不再一 ...
- PHP的两个科学计数法转换为字符串的方法
不常用,所以整理在这里,分享给同行使用 方法一:取尾数法 public function NumToStr($num) { if (stripos($num, 'e') === false) retu ...