【问题标题】:Which way is thread-safe and has better performance to use, ContainsKey or string indexer?哪种方式是线程安全的并且具有更好的使用性能,ContainsKey 还是字符串索引器?
【发布时间】:2018-04-04 10:56:08
【问题描述】:

我有如下收藏

private static readonly Dictionary<string,object> _AppCache = new Dictionary<string,object>;

然后我想知道哪个更好用于检查键是否存在(我的键都没有空值)

  1. _AppCache.ContainsKey("x")
  2. _AppCache["x"] != null

此代码可能通过不同数量的线程访问

整个代码是:

public void SetGlobalObject(string key, object value)
{
    globalCacheLock.EnterWriteLock();
    try
    {
        if (!_AppCache.ContainsKey(key))
        {
            _AppCache.Add(key, value);
        }
    }
    finally
    {
        globalCacheLock.ExitWriteLock();
    }
}

更新

我更改了代码以使用字典将问题的焦点集中在Conatinskey or Indexer

【问题讨论】:

  • 你应该使用Dictionary<K,V>
  • @SLaks 背后的原因是什么?
  • @RezaRahmati 类型安全。
  • @SLaks Hastable 不是类型安全的?字典不是自己的哈希表吗?
  • @RezaRahmati 使用Hashtable,你可以使用_AppCache.Add(5, value),因为很明显你每个人都想使用string 键。如果你使用Dictionary<string, object>,那行代码会被编译器捕获。

标签: c# .net hashtable


【解决方案1】:

我不同意其他人使用Dictionary 的建议。但是,要回答您的问题,我认为您应该使用ContainsKey 来检查密钥是否存在,原因有很多

  • 这就是 ContainsKey 的具体用途
  • 要使_AppCache["x"] != null 工作,您的应用程序必须在非强制假设下运行(没有值将是空的)。这个假设现在可能成立,但未来的维护者可能不知道或不理解这个关键假设,从而导致不直观的错误
  • ContainsKey 的处理量略少,尽管这并不重要

这两种选择都不是线程安全的,因此这不是决定因素。为此,您要么需要使用锁定,要么使用ConcurrentDictionary

如果您转到Dictionary(根据您的问题更新),答案更倾向于ContainsKey。如果使用 index 选项,则必须捕获异常以检测键是否不在字典中。 ContainsKey 在您的代码中会更直接。

当密钥在Dictionary 中时,ContainsKey 的效率略高。这两个选项首先调用一个内部方法 FindEntry。对于ContainsKey,它只返回结果。对于 index 选项,它还必须检索该值。在键不在字典中的情况下,索引选项的效率会相当低,因为它会引发异常。

【讨论】:

    【解决方案2】:

    您应该使用ConcurrentDictionary 而不是字典,它本身是线程安全的。因此,您不需要锁,这(通常*)可以提高性能,因为锁定机制相当昂贵。

    现在,仅检查条目是否存在,我推荐 ContainsKey,无论您使用哪个(并发)字典:

    _AppCache.ContainsKey(key)
    

    但是你分两步做的事情可以使用并发字典通过GetOrAdd一步完成:

    _AppCache.GetOrAdd(key, value);
    

    这两种操作都需要锁:

    public void SetGlobalObject(string key, object value)
    {
        _AppCache.GetOrAdd(key, value);
    }
    

    这不仅(可能 *)表现更好,而且我认为它更清楚地表达了你的意图,不那么混乱。

    (*) 在这里使用“可能”和“一般”来强调这些数据结构确实具有大量的性能优化,但是必须始终衡量您的特定情况中的性能。

    【讨论】:

      【解决方案3】:

      如果您担心线程安全,您应该看看ConcurrentDictionary 类。

      如果您不想使用 ConcurrentDictionary,则必须确保同步对常规 Dictionary<K,V> 实例的访问。这意味着,通过锁定每个写入和读取操作,确保没有 2 个线程可以对您的字典进行多次访问。

      例如,如果您想以线程安全的方式向常规字典添加内容,则必须这样做:

      private readonly object _sync = new object();
      
      // ...
      
      
      lock( _sync )
      {
          if( _dictionary.ContainsKey(someKey) == false )
          {
               _dictionary.Add(someKey, somevalue);
          }
      }
      

      自从引入通用 Dictionary<K,V> 类后,您不应该再使用 Hashtable 了,因此在 .NET 2.0 中引入了类型安全的替代方案

      使用Dictionary<K,V> 时需要注意:当您要检索与给定键关联的值时,如果该指定键没有条目,字典将抛出异常,而哈希表将返回 null案例。

      【讨论】:

        【解决方案4】:

        您显然是在检查该密钥是否存在。在这种情况下,如果密钥不存在,_AppCache["x"] != null 会给你一个KeyNotFoundException,这可能是不可取的。如果你真的想检查密钥是否存在,而不是通过检查产生异常,你必须使用_AppCache.ContainsKey("x")。为了检查字典或哈希表中是否存在密钥,我会坚持使用ContainsKey。如果!= null 更快,则性能上的任何差异都会被额外的代码抵消,以处理如果密钥确实不存在的异常。

        实际上,_AppCache["x"] != null 并不是在检查键是否存在,而是在检查键“x”存在的情况下,关联的值是否为空。

        两种方式(尽管完成不同的任务)都不会在线程安全方面为您带来任何优势。

        如果您使用ConcurrentDictionary,所有这些都是正确的 - 线程安全没有区别,两种方法完成不同的事情,使用!=null 检查的任何可能收益都被处理异常的额外代码所抵消。所以,请使用ContainsKey

        【讨论】:

        • 谢谢,因为我最初的问题是关于Hashtable,它不会抛出异常,现在换成字典你是对的
        • 你是对的,所以异常处理已经结束,但我想说的是,类型安全的好处抵消了能够使用!= null 逻辑的任何好处。所以使用ConcurrentDictionary 并使用ContainsKey
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-12
        • 1970-01-01
        • 1970-01-01
        • 2010-10-17
        • 1970-01-01
        • 2016-12-10
        • 2016-07-16
        相关资源
        最近更新 更多