上一篇文章“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. Oracle SQL: TO_CHAR and TO_NUMBER 笔记

    (1)select TO_CHAR(123.56,'999.9') from dual; will return 123.6select TO_NUMBER('123.56','999.9') fro ...

  2. Python:集合

    set集合: 特点:无序,元素不重复 功能:关系测试,去重 集合创建: >>> s = set('python') >>> s {'p', 't', 'y', 'h ...

  3. mysql的一个特殊问题 you can't specify target table 'cpn_regist' for update in FROM clause

    今天在操作数据库的时候遇到了一个问题,sql语句如下: UPDATE cpn_yogurt_registration SET dep1Name = '1' WHERE `key` in  (SELEC ...

  4. Photoshop 使用曲线

    曲线表示的是图像的明度, 通过信息办的 HSB 信息可以看到调整曲线时整个图像明度的变化 曲线的左下角表示图片的暗部, 右下角表示图片的高光部 而曲线本身的纵坐标则表示这个部分的明度, 例如将曲线的左 ...

  5. JavaScript密码复杂度

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...

  6. 【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

    这次的题总体上相对前三次偏简单.由于实力有限,就分析前四题.     试题编号:    201503-1 试题名称:    图像旋转 时间限制:    5.0s 内存限制:    256.0MB 问题 ...

  7. 实现公告栏无缝滚动的js代码(转)

    <!DOCTYPE HTML> <html> <head> <meta charset="gb2312" /> <title& ...

  8. EasyUI DataGrid 配置参数

    var queryParams = $('#SBDiv_1_DateGrid').datagrid('options').queryParams; queryParams.SearchTime = & ...

  9. Linux下cutecom使用USB转串口线

    http://www.cnblogs.com/pang123hui/archive/2011/05/29/2309888.html 在Linux下的串口调试一直使用minicom,虽说Linux的精髓 ...

  10. MyEclipse取消验证Js的两种途径.

    前言:有时我们通过js写一个web工程的相关页面时感觉很卡,修改内存也不行下面有俩种解决方法: 1.  选中当前工程—properties—MyEclipse—validation—Excluded ...