• ConcurrentDictionary实现


    .Net4 增加的System.Collection.Concurrent线程安全的集合实现,这儿有MS的性能测试报告:Thread-safe Collections in .NET Framework 4 and Their Performance Characteristics。总的来说效率还是很不错的,为了提高效率用了一些技巧,接口上也多是TryXXX。

             ConcurrentDictionary采用Level-Lock方式,和Dictionary一样还是只有一个buckets,只是内部lock时,采用locknum=key.GetHashcode%lockCount, lock(objects[num]),方式,默认情况下DefaultConcurrencyLevel=4 * Environment.ProcessorCount;  也就是4*CPU核心数。相比于一个lock object,在高并发使用多个lock object可以很大减少lock等待的可能;但在整个集合操作(如:Count,Clear,Expend..)还是需要全部加锁后操作。

             为啥接口都变成TryXXX呢:因为如Add 和Remove之间存在竞争,不能再像单线程集合那样简单的抛出异常,不能抛出异常那就得要一个是否操作成功的返回码~

    下面是节点数据结构,算法也有很大的区别,这里使用类,hashtable ,Dictionary都用的是struct。问题在于如果使用HashTable双hash算法,对同一个数组就没法做Level啦。Dictionary分别两个数组Bluckets和Entitys,同样无法区分level。至于hash算法比起前两者都要简单不少呢。所以采用了class 可以使用m_next连接方式解决冲突,m_ext 使用volatile可解决多线程缓存同步问题。

    来咱来计算一下内存占用问题吧使用class方式无疑对于简单字段要占用多不少内存,就拿int-int来算:

      Dictionary 4*5=20字节/node

    ConcurrentDictionary 在x64下:8+4+4+4+8+8+8=42字节/node 要多占不少内存那。不过对于Node=null时,空位多的时候也能节省部分内存,不过一般空位多了不应该~

    ------  blucket(8)+Node(key(4)+value(4)+hash(4)+next(8)+methodRef(8))+syncblk(8))

    *有个想法咱吧Dictionary改造一下位Level Lock,用多个Dict,前面文章有测试效果,还不错。

       1:  private class Node //不再是struck 了呢
       2:  {
       3:      internal TKey m_key;
       4:      internal TValue m_value;
       5:      internal volatile Node m_next;  //在这儿volatile很重要
       6:      internal int m_hashcode;
       7:      internal node()
       8:      {
       9:          this.m_key = key;
      10:          this.m_value = value;
      11:          this.m_next = next;
      12:          this.m_hashcode = hashcode;
      13:      }
      14:  }
     
     
    下面是Add的实现:
       1:  private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue)
       2:  {
       3:      int hashCode = this.m_comparer.GetHashCode(key);
       4:      checked
       5:      {
       6:          ConcurrentDictionary<TKey, TValue>.Node[] buckets;
       7:          bool flag;
       8:          while (true)
       9:          {
      10:              buckets = this.m_buckets;
      11:              int num;
      12:              int num2;
      13:              this.GetBucketAndLockNo(hashCode, out num, out num2, buckets.Length);//计算Hash地址和新的节点落在那个Lock上面
      14:              flag = false;
      15:              bool flag2 = false;
      16:              try
      17:              {
      18:                  if (acquireLock)
      19:                  {
      20:                      Monitor.Enter(this.m_locks[num2], ref flag2);
      21:                  }
      22:                  if (buckets != this.m_buckets)
      23:                  {
      24:                      continue; //这儿很重要Enter之前如果不做这个判断,可能因为字段扩容而丢失值
      25:                  }
      26:                  ConcurrentDictionary<TKey, TValue>.Node node = null;
      27:                  for (ConcurrentDictionary<TKey, TValue>.Node node2 = buckets[num]; node2 != null; node2 = node2.m_next)
      28:                  {
      29:                      if (this.m_comparer.Equals(node2.m_key, key))
      30:                      {
      31:                          if (updateIfExists)
      32:                          {
      33:                              ConcurrentDictionary<TKey, TValue>.Node node3 = new ConcurrentDictionary<TKey, TValue>.Node(node2.m_key, value, hashCode, node2.m_next);
      34:                              if (node == null)
      35:                              {
      36:                                  buckets[num] = node3;
      37:                              }
      38:                              else
      39:                              {
      40:                                  node.m_next = node3;
      41:                              }
      42:                              resultingValue = value;
      43:                          }
      44:                          else
      45:                          {
      46:                              resultingValue = node2.m_value;
      47:                          }
      48:                          return false;
      49:                      }
      50:                      node = node2;
      51:                  }
      52:                  buckets[num] = new ConcurrentDictionary<TKey, TValue>.Node(key, value, hashCode, buckets[num]);
      53:                  this.m_countPerLock[num2]++;
      54:                  if (this.m_countPerLock[num2] > buckets.Length / this.m_locks.Length)//这个扩容标准比Dictionary要严一点哈~
      55:                  {
      56:                      flag = true;
      57:                  }
      58:              }
      59:              finally
      60:              {
      61:                  if (flag2)
      62:                  {
      63:                      Monitor.Exit(this.m_locks[num2]);
      64:                  }
      65:              }
      66:              break;
      67:          }
      68:          if (flag)
      69:          {
      70:              this.GrowTable(buckets);
      71:          }
      72:          resultingValue = value;
      73:          return true;
      74:      }
      75:  }

    比较有意思的是枚举器的实现,因为我们这个字段并没有维护version版本号哦~~~,看看他是怎么解决并发问题的:

       1:  public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
       2:  {
       3:      ConcurrentDictionary<TKey, TValue>.Node[] buckets = this.m_buckets;
       4:      for (int i = 0; i < buckets.Length; i++)
       5:      {
       6:          ConcurrentDictionary<TKey, TValue>.Node node = buckets[i];
       7:          Thread.MemoryBarrier();
       8:          while (node != null)
       9:          {
      10:              yield return new KeyValuePair<TKey, TValue>(node.m_key, node.m_value);
      11:              node = node.m_next;
      12:          }
      13:      }
      14:      yield break;
      15:  }

    我们看着这个实现完全是无锁的,但也是不准确的,因为如果正好碰上扩展就不对了,不过对于并发字典这没什么问题。

  • 相关阅读:
    开始学习编写用于 Windows SideShow 设备的小工具【转】
    Windows Mobile 6.5 Developer Tool Kit 下载
    Microsoft Security Essentials 微软免费杀毒软件下载
    SQL Server 2008 空间数据存储摘抄(SRID 点 MultiPoint LineString MultiLineString 多边形 MultiPolygon GeometryCollection)
    Vista Sidebar Gadget (侧边栏小工具)开发教程 (2)
    Vista Sidebar Gadget (侧边栏小工具)开发教程 (4)
    负载测试、压力测试和性能测试的异同
    Windows Server 2008 Vista Sidebar Gadget (侧边栏小工具) 入门开发实例
    Silverlight Tools 安装失败 解决办法
    SQL Server 2008 空间数据库 空间索引概念及创建(取自帮助)
  • 原文地址:https://www.cnblogs.com/lulu/p/2519675.html
Copyright © 2020-2023  润新知