ConcurrentDictionary.Count > 0 是否与 ConcurrentDictionary.Any() 相同?

Is ConcurrentDictionary.Count > 0 the same as ConcurrentDictionary.Any()?

如果我有一个 ConcurrentDictionary 实例,我使用 Count 属性 还是 LINQ 的 Any() 有关系吗?我宁愿写 dict.Any() 而不是 dict.Count > 0 因为我认为 Any() 更具描述性。

我只关心正确性,不关心性能。用例是

void process()
{
   if (concurrentDictionary.Count <= 0) // or !Any() ?
      return; // dictionary is empty, nothing to do

   // ...
}

does it matter whether I use the Count property or LINQ's Any()

没有。它们在功能上是相同的,性能差异应该很小。使用最恰当地传达含义的任何内容,并且只有在存在对整个应用程序的性能有重大影响的性能问题时才更改它。

Count 将在调用 属性 时计算字典中的项目。

Any 将调用 ConcurrentDictionary.GenEnumerator() 以查看字典是否有任何项目。根据 documentation,返回的枚举器将反映在调用 GetEnumerator() 后对字典所做的更改。

因此,如果在调用 Any 和在 Any 内调用 MoveNext 之间添加了一个项目,那么从理论上讲,他们可能会得到不同的答案。不过那个时间window应该这么短,可能性应该很小。

还有谁说的对呢?如果您可以 AnyCount 完全相同的时间 添加一个项目,集合是否为空?

您必须对它们进行基准测试,因为 Any() 类似于

using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
    if (enumerator.MoveNext())
    {
        return true;
    }
}

return false;

所以它需要枚举,对于ConcurrentDictionary来说有点复杂,但即使是ConcurrentDictionaryCount也没有缓存,它看起来很复杂。

我要补充一点,Count 仍然必须遍历一些内部结构(如在数组中)以获取对整个字典的锁定,而 Any() 将在第一个非- 空桶。我会说,对于大字典,Count 较慢,而对于小字典,它更快。

更正:Count 在计数之前需要锁定所有字典。它确实调用 this.AcquireAllLocks().

请记住,两种方法的结果都可以在方法 return 之前被伪造,因为嘿...并发! :-)

问题 Are IEnumerable Linq methods thread-safe? 解决了以下事实:LINQ 查询上的 IEnumerable 方法在没有保持特定锁保护集合的情况下不是线程安全的。

您可以查看 reference code for ConcurrentDictionary to see that the enumerator does not provide a thread-safe snapshot. Additional the MSDN Documentation for ConcurrentDictionary.GetEnumerator 状态:

The enumerator returned from the dictionary is safe to use concurrently with reads and writes to the dictionary, however it does not represent a moment-in-time snapshot of the dictionary. The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called

Count 属性 完全锁定字典,returns 得到一致的结果。

因此,根据您是否要锁定 运行 Any() 的字典,检查 Count > 0.

可能更清晰