改进ConcurrentDictionary并行使用的性能
上一篇文章“ConcurrentDictionary 对决 Dictionary+Locking”中,我们知道了 .NET 4.0 中提供了线程安全的 ConcurrentDictionary<TKey, TValue> 类型,并在某些特定的使用条件下会产生问题。
在 ConcurrentDictionary<TKey, TValue> 类中有一个方法 GetOrAdd ,用于尝试获取一个键值,如果键值不存在则添加一个。其方法签名如下:
public TValue GetOrAdd(
TKey key,
Func<TKey, TValue> valueFactory
) Parameters
key
Type: TKey
The key of the element to add. valueFactory
Type: System.Func<TKey, TValue>
The function used to generate a value for the key
通常,我们会通过如下这种方式来使用:
ConcurrentDictionary<string, ExpensiveClass> dict1
= new ConcurrentDictionary<string, ExpensiveClass>(); string key1 = "";
ExpensiveClass value1 = dict1.GetOrAdd(
key1,
(k) => new ExpensiveClass(k));
这种使用方式会产生一个问题,就是如果特定的类的构造过程比较昂贵(资源消耗、时间消耗等),在并行运行条件下,当第一个线程尝试获取该键值时,发现不存在后开始构建该对象,而在构建的同时,另外一个线程也尝试获取该键值,发现不存在后也开始构建该对象,当第一个线程构造完毕后将对象添加至字典中,而第二个对象也构造完毕后会再次检测字典中是否存在该键值,因为键值已经存在,所以将刚创建完毕的对象直接丢弃,而使用已存在的对象,这造成了对象构造过程中的浪费。如果是关注性能和资源的应用,此处就是一个需要改进的点。
我们假设这个类叫 ExpensiveClass 。
public class ExpensiveClass
{
public ExpensiveClass(string id)
{
Id = id; Console.WriteLine(
"Id: [" + id + "] called expensive methods " +
"which perhaps consume a lot of resources or time.");
} public string Id { get; set; }
}
类实例化的构造过程为什么昂贵可能有很多中情况,最简单的例子可以为:
- 访问了数据库,读取了数据,并缓存了数据。
- 访问了远程服务,读取了数据,并缓存了数据。
- 将磁盘中的数据加载到内存中。
改进方式1:使用Proxy模式
我们可以使用 Proxy 模式来包装它,通过 Proxy 中间的代理过程来隔离对对象的直接创建。
public class ExpensiveClassProxy
{
private string _expensiveClassId;
private ExpensiveClass _expensiveClass; public ExpensiveClassProxy(string expensiveClassId)
{
_expensiveClassId = expensiveClassId;
} public ExpensiveClass XXXMethod()
{
if (_expensiveClass == null)
{
lock (_expensiveClass)
{
if (_expensiveClass == null)
{
_expensiveClass = new ExpensiveClass(_expensiveClassId);
}
}
}
return _expensiveClass;
}
}
改进方式2:使用Lazy<T>模式
这种方式简单易用,并且同样解决了问题。
ConcurrentDictionary<string, Lazy<ExpensiveClass>> dict2
= new ConcurrentDictionary<string, Lazy<ExpensiveClass>>(); string key2 = "";
ExpensiveClass value2 = dict2.GetOrAdd(
key2,
(k) => new Lazy<ExpensiveClass>(
() => new ExpensiveClass(k)))
.Value;
在并行的条件下,同样也存在构造了一个 Lazy<ExpensiveClass> 然后丢弃的现象,所以这种方式是建立在,构造 Lazy<T> 对象的成本要小于构造 ExpensiveClass 的成本。
改进ConcurrentDictionary并行使用的性能的更多相关文章
- Java 8 (6) Stream 流 - 并行数据处理与性能
在Java 7之前,并行处理集合非常麻烦.首先你要明确的把包含数据的数据结构分成若干子部分,然后你要把每个子部分分配一个独立的线程.然后,你需要在恰当的时候对他们进行同步来避免竞争,等待所有线程完成. ...
- 《Java 8 in Action》Chapter 7:并行数据处理与性能
在Java 7之前,并行处理数据集合非常麻烦.第一,你得明确地把包含数据的数据结构分成若干子部分.第二,你要给每个子部分分配一个独立的线程.第三,你需要在恰当的时候对它们进行同步来避免不希望出现的竞争 ...
- SQL并行与否的性能差异
比较两种代码,核心代码相同,其中一个使用变量保存查询出的结果,另一个直接输出.使用同一变量时,强迫SQL放弃了并行,使用了循环. 测试结果 表'#1699586C'.扫描计数1,逻辑读取186 次 ...
- Java 8并行流的性能陷阱
并行化流被分成多个块,每个块独立处理,结果在最后汇总. CPU密集型代码如下: private long countPrimes(int max) { return range(1, max) ...
- .NET 7 性能改进 -- 至今为止最快的.NET平台
2022年8月31日 Stephen Toub 发布的关于 .NET 7 性能改进的博客, 核心主题是 .NET 7 速度很快. 这篇博客非常的长,我尝试将它拷贝到Word 里,拷贝的时间都花了几分钟 ...
- .NET性能系列文章一:.NET7的性能改进
这些方法在.NET7中变得更快 照片来自 CHUTTERSNAP 的 Unsplash 欢迎阅读.NET性能系列的第一章.这一系列的特点是对.NET世界中许多不同的主题进行研究.比较性能.正如标题所说 ...
- SQL Server 2016中In-Memory OLTP继CTP3之后的新改进
SQL Server 2016中In-Memory OLTP继CTP3之后的新改进 转译自:https://blogs.msdn.microsoft.com/sqlserverstorageengin ...
- 【转发】关于Java性能的9个谬论
转载请注明出处,感谢大家的支持!本文来自优优码:http://www.uucode.net/201502/9%e4%b8%aa%e8%b0%ac%e8%ae%ba Java的性能有某种黑魔法之称.部分 ...
- Java性能调优
一.JVM内存模型及垃圾收集算法 1.根据Java虚拟机规范,JVM将内存划分为: New(年轻代) Tenured(年老代) 永久代(Perm) 其中New和Tenured属于堆内存,堆内存会从JV ...
随机推荐
- KBMMW 4.90.00 发布
kbmMW is a portable, highly scalable, high end application server andenterprise architecture integra ...
- 在bootstrap ace样式框架上修改的后台管理型模板(Tab页后台管理模板)
后台管理模板开始用frameset布局,但是有时候会遮挡比如上面导航或者左边导航的二级三级弹出菜单,因为宽度被限制了,所以有时候就用easyui或者ext的,但是样式不好看,然后看到了bootstra ...
- Android源代码结构分析
Google提供的Android包含了:Android源代码,工具链,基础C库,仿真环境,开发环境等,完整的一套.第一级别的目录和文件如下所示:----------------├── Makefile ...
- oracle常用操作指令
1.cmd sqlplus /nolog; 2.conn sys/ as sysdba; 3.create user query identified by query;//创建用户 4.al ...
- EF联合查询的新用法
用EF很多年了,做联合查询时,只知道linq和lambda两种语法,今天朋友发了一个链接,打开看后发现是EF内置的新的关于联合查询的方法,赶紧抄录下来,以备后用. 现在先把这几种方法,各写一个例子,便 ...
- (十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- 读取当前键值,并赋值给LED
/********************************* 代码功能:读取当前键值,并赋值给LED 使用函数: digitalRead(数字输入端口号); 创作时间:2016*10*07 作 ...
- MATLAB - 运算符
1.关系运算符用来比较两个数之间的大小关系,在Matlab中的关系运算符包括: < 小于 <= 小于或等于 > 大于 >= 大于或等于 == ...
- JDBC学习
JDBC全称 Java DataBase Connectivity(java数据库连接)可以为多种数据库提供统一的访问: 步骤:1:加载驱动程序: 加载Mysql驱动:Class.forName(&q ...
- Python全栈之路4--内置函数--文件操作
上节重点回顾: 判断对象是否属于某个类,例如: 列表中有个数字,但是循环列表判断长度,用len会报错;因为int不支持len,所以要先判断属于某个类,然后再进行if判断. # isinstance(对 ...