浅析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这个关键字的用法. ...
随机推荐
- 【后缀自动机】hihocoder1441 后缀自动机一·基本概念
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi:今天我们来学习一个强大的字符串处理工具:后缀自动机(Suffix Automaton,简称SAM).对于一个字符串 ...
- django2与1的差别和视图
django1与2路由的差别 在django1中的url在django2中为re_path django2中新增了path 1.from django.urls import path 2.不支持正则 ...
- sqlserver 计算数据库时间差
介绍:datediff(datepart,startdate,enddate) 返回间隔datepart 的数 SELECT datediff(yy,'2010-06-1 10:10',GETDATE ...
- 使用Python SocketServer快速实现多线程网络服务器
Python SocketServer使用介绍 1.简介: SocketServer是python的一个网络服务器框架,可以减少开发人员编写网络服务器程序的工作量. SocketServer总共有4个 ...
- Oracle一条SQL语句时快时慢
今天碰到一个非常奇怪的问题问题,一条SQL语句在PL/SQL developer中很慢,需要9s,问题SQL: SELECT * FROM GG_function_location f WHERE f ...
- python笔记10-多线程之线程同步(锁lock)
前言 关于吃火锅的场景,小伙伴并不陌生,吃火锅的时候a同学往锅里下鱼丸,b同学同时去吃掉鱼丸,有可能会导致吃到生的鱼丸. 为了避免这种情况,在下鱼丸的过程中,先锁定操作,让吃火锅的小伙伴停一会,等鱼丸 ...
- 在matlab中clear,clc,clf,hold作用介绍
clear 是清变量: clc 只清屏: clf 清除图形窗口上的旧图形: hold on 是为了显示多幅图像时,防止新的窗口替代旧的窗口: close 关闭所有显示的图像.
- 为TextView设置两种状态,程序中可以动态切换
经常会需要用文字的两种状态来表示当前系统的某两种状态.比如: 这里的第一个TextView和后两个TextView就表示了两种状态.我们可以在程序的动态的切换状态(而不是直接修改颜色) ...
- python 调试模式pdb(转)
标准库的pdb pdb是Python自带的一个库,为Python程序提供了一种交互式的源代码调试功能,包含了现代调试器应有的功能,包括设置断点.单步调试.查看源码.查看程序堆栈等.如果读者具有C或C+ ...
- 视差映射 parrallax mapping
算个新的uv在heightmap https://learnopengl.com/Advanced-Lighting/Parallax-Mapping https://blog.csdn.net/so ...