上一篇文章“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. Deep Learning(深度学习)学习笔记整理

    申明:本文非笔者原创,原文转载自:http://www.sigvc.org/bbs/thread-2187-1-3.html 4.2.初级(浅层)特征表示 既然像素级的特征表示方法没有作用,那怎样的表 ...

  2. 简单的javascript--test2

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

  3. ASP.NET上传大文件的问题

    原文:http://www.cnblogs.com/wolf-sun/p/3657241.html?utm_source=tuicool&utm_medium=referral 引言 之前使用 ...

  4. C#去掉list集合中的重复数据

    List<string> conList= new List<string>(); List<string> listII = new List<string ...

  5. 泛型的上限和下限的Demo

    Main Class package Comparator.Bean; import java.math.BigDecimal; import java.util.List; import java. ...

  6. 如何查看linux内核的版本号?

    zz:http://www.cnblogs.com/hnrainll/archive/2011/06/08/2074957.html 方法一: 命令: uname -a 作用: 查看系统内核版本号及系 ...

  7. IntelliJ IDEA 16 本地LicenseServer激活(破解)

    IntelliJ IDEA 16 本地LicenseServer激活(破解) IntelliJ IDEA 是Java开发利器,用社区版不爽,干催就用旗舰版,这个是收费的,需要licence. 网上找到 ...

  8. C#拼接地图瓦片

    为了在AE程序中使用离线的电子地图,思路如下: 利用下载工具下载地图切片,然后利用C#进行切片拼接成一张图片,最后使用ArcMap进行地理配准,然后发布成ArcGIS Server 切片服务供程序使用 ...

  9. Scrum会议10.19

    Scrum会议 组名称:好好学习 项目名称:记账本 参会成员:林莉(Master)胡丽娜 宫丽君 汪东涵 时间:2016.10.19 已完成内容: 1.完成新项目的查找,查找学姐的代码和项目. 2.理 ...

  10. 自定义安装php开发环境(1)--apache和php整合

    第一步:安装apache 第二步:下载php核心包php-5.3.3-Win32-VC6-x86.zip.并放入开发环境文件夹C:/phpenv/文件夹下 第三步: 将apache 和php 整合 也 ...