Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

If I have a ConcurrentDictionary instance, does it matter whether I use the Count property or LINQ's Any()? I'd rather write dict.Any() instead of dict.Count > 0 as I think Any() is more descriptive.

I'm only concerned about correctness, not performance. The use case is

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

   // ...
}
like image 476
Ðаn Avatar asked Mar 16 '23 13:03

Ðаn


2 Answers

The question Are IEnumerable Linq methods thread-safe? addresses the fact that the IEnumerable methods on LINQ queries are not thread safe without a specific lock being kept protecting the collection.

You can look at the reference code for ConcurrentDictionary to see that the enumerator does not provide a thread-safe snapshot. Additional the MSDN Documentation for ConcurrentDictionary.GetEnumerator states:

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

The Count property takes a full lock on the dictionary and returns a consistent result.

So depending on whether you want to take a lock on the dictionary to run Any() it is probably cleaner to check for Count > 0.

like image 93
Steve Mitcham Avatar answered Mar 19 '23 01:03

Steve Mitcham


You will have to benchmark them, because the Any() is something like

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

return false;

so it requires enumeration, that for ConcurrentDictionary is something complex, but even the Count of ConcurrentDictionary isn't cached and it seems to be pretty complex.

I'll add that the Count must still traverse some internal structures (as in an array of) aquiring a lock on the whole dictionary, while the Any() will stop at the first non-empty bucket. I'll say that for a big dictionary, Count is slower, while for a small one it is faster.

Correction: the Count aquires a lock on all the dictionary before counting. it does call this.AcquireAllLocks().

Remember that the result of both method could be falsified before the methods return, because hey... concurrency! :-)

like image 44
xanatos Avatar answered Mar 19 '23 03:03

xanatos