上一篇文章“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并行使用的性能的更多相关文章

  1. Java 8 (6) Stream 流 - 并行数据处理与性能

    在Java 7之前,并行处理集合非常麻烦.首先你要明确的把包含数据的数据结构分成若干子部分,然后你要把每个子部分分配一个独立的线程.然后,你需要在恰当的时候对他们进行同步来避免竞争,等待所有线程完成. ...

  2. 《Java 8 in Action》Chapter 7:并行数据处理与性能

    在Java 7之前,并行处理数据集合非常麻烦.第一,你得明确地把包含数据的数据结构分成若干子部分.第二,你要给每个子部分分配一个独立的线程.第三,你需要在恰当的时候对它们进行同步来避免不希望出现的竞争 ...

  3. SQL并行与否的性能差异

    比较两种代码,核心代码相同,其中一个使用变量保存查询出的结果,另一个直接输出.使用同一变量时,强迫SQL放弃了并行,使用了循环.   测试结果 表'#1699586C'.扫描计数1,逻辑读取186 次 ...

  4. Java 8并行流的性能陷阱

    并行化流被分成多个块,每个块独立处理,结果在最后汇总. CPU密集型代码如下: private long countPrimes(int max) {     return range(1, max) ...

  5. .NET 7 性能改进 -- 至今为止最快的.NET平台

    2022年8月31日 Stephen Toub 发布的关于 .NET 7 性能改进的博客, 核心主题是 .NET 7 速度很快. 这篇博客非常的长,我尝试将它拷贝到Word 里,拷贝的时间都花了几分钟 ...

  6. .NET性能系列文章一:.NET7的性能改进

    这些方法在.NET7中变得更快 照片来自 CHUTTERSNAP 的 Unsplash 欢迎阅读.NET性能系列的第一章.这一系列的特点是对.NET世界中许多不同的主题进行研究.比较性能.正如标题所说 ...

  7. SQL Server 2016中In-Memory OLTP继CTP3之后的新改进

    SQL Server 2016中In-Memory OLTP继CTP3之后的新改进 转译自:https://blogs.msdn.microsoft.com/sqlserverstorageengin ...

  8. 【转发】关于Java性能的9个谬论

    转载请注明出处,感谢大家的支持!本文来自优优码:http://www.uucode.net/201502/9%e4%b8%aa%e8%b0%ac%e8%ae%ba Java的性能有某种黑魔法之称.部分 ...

  9. Java性能调优

    一.JVM内存模型及垃圾收集算法 1.根据Java虚拟机规范,JVM将内存划分为: New(年轻代) Tenured(年老代) 永久代(Perm) 其中New和Tenured属于堆内存,堆内存会从JV ...

随机推荐

  1. win 7 IIS 配置

    http://jingyan.baidu.com/article/219f4bf723bcb2de442d38ed.html win7旗舰版系统 点击开始→控制面板,然后再点击程序,勿点击卸载程序,否 ...

  2. 简单的javascript--test2

    插件: jquery, sweetalert (http://t4t5.github.io/sweetalert/ ) 1.index.html <!DOCTYPE HTML> <h ...

  3. pcA降维算法

    http://ufldl.stanford.edu/wiki/index.php/主成分分析 if ~exist('train_IM_all','var')||~exist('train_LA_all ...

  4. Json格式转化为string格式

    今天在学习Jsonp的时候,想通过服务端返回一段json数据,因为使用的服务端是NodeJs,那么就需要在js文件中实现返回json.(这里不懂的同学,可以先了解一下NodeJs的基础概念,在这里,我 ...

  5. UIScrollViewDelegate

    一.执行顺序:(scrollView加后面的)willBeginDragging         // 将要开始拖拽,手指已经放在view上并准备拖动的那一刻 DidScroll           ...

  6. MATLAB - 图像基本操作

    1.读入一幅图像 I = imread('lena.jpg');%给出图片路径即可,I是一个三维矩阵 J = rgb2gray(I); figure, imshow(I), figure, imsho ...

  7. 笔记本Linux推荐

    1.CUB LINUX Cub Linux 是一个最好的选择.他的前身来自著名的 Chromium OS , Cub Linux 能够运行在各种各样的笔记本上面.即便是早年的老机,亦或是现在的新机.从 ...

  8. 清除SQLServer日志的两种方法

    日志文件满而造成SQL数据库无法写入文件时,可用两种方法:一种方法:清空日志.1.打开查询分析器,输入命令DUMP TRANSACTION 数据库名 WITH NO_LOG2.再打开企业管理器--右键 ...

  9. ZT 第一范式,第二范式,第三范式

    第一范式,第二范式,第三范式 Posted on 2012-05-09 16:30 GISerYang 阅读(6472) 评论(0) 编辑 收藏 第一范式 存在非主属性对码的部分依赖关系 R(A,B, ...

  10. hdu 5818 (优先队列) Joint Stacks

    题目:这里 题意: 两个类似于栈的列表,栈a和栈b,n个操作,push a x表示把数x放进a栈的栈底,pop b 表示将栈b的栈顶元素取出输出,并释放这个栈顶元素,merge a b表示把后面的那个 ...