Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When Iterating Over ConcurrentDictionary and only reading, is ConcurrentDictionary locked?

  1. I have a ConcurrrentDictionary created as an application object in my web app. and it is shared among sessions. (Basically serves as a repository.)
  2. At times a new item is added to the dictionary by any available session.

Allow Admin View Only

Now, I want to allow an admin to list all the values in the dictionary, but the admin will not add or remove items, instead I will only provide a way for the admin to see the items via a read of the collection by iterating through the items.

(Pseudo)Code would look something like this:

foreach (var e in EmployeeCache.Instance.AllEmployees)
{
     Console.WriteLine(e.Key);
}

My question is this:

If I iterate through the items does the ConcurrentDictionary get locked while it is being read from? In other words, is the ConcurrentDictionary locked so that other sessions would be unable to add or remove while the admin code is simply iterating through the ConcurrentDictionary?

If Not Locked, Can You Explain

If you believe it is not locked, can you give a quick summary of how it does this? For example, does it create a copy of the ConcurrentDictionary for the read-only action and then allow the read iterations to run -- understanding that concurrent changes to the real dictionary will not be seen?

What I'm Trying To Determine

I'm trying to understand the impact of providing a ConcurrentDictionary viewer which could be refreshed often by an Admin. I.E. If they refreshed it often enough could it impact the performance of the web app. as sessions are waiting for the object to unlock so they can add/remove items?

like image 301
raddevus Avatar asked Jun 16 '14 15:06

raddevus


Video Answer


2 Answers

This is how ConcurrentDictionary.GetEnumerator Is implemented:

/// <remarks>
/// 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 <see cref="GetEnumerator"/> was called.
/// </remarks>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
    Node[] buckets = m_tables.m_buckets;

    for (int i = 0; i < buckets.Length; i++)
    {
        // The Volatile.Read ensures that the load of the fields of 'current'
        // doesn't move before the load from buckets[i].
        Node current = Volatile.Read<Node>(ref buckets[i]);

        while (current != null)
        {
            yield return new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
            current = current.m_next;
        }
    }
}

As you see, the iteration is lock free, and simply yields a immutable struct (KeyValuePair) which is returned to the caller for each iteration. That is why it cant guarantee a snapshot-in-time of the ConcurrentDictionary

This will definitely not have a performance effect on adding/updating new values while iterating, but it simply cant guarantee that your admin will see the most updated snapshot of the dictionary.

  1. You can browse the rest of the source code yourself via http://sourceof.net
  2. And you can also check out Inside the Concurrent Collections: ConcurrentDictionary by Simon Cooper.
  3. Are all of the new concurrent collections lock-free?
like image 147
Yuval Itzchakov Avatar answered Oct 22 '22 21:10

Yuval Itzchakov


This is what documentation is saying:

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.

http://msdn.microsoft.com/en-us/library/dd287131(v=vs.110).aspx

So, if you want "snapshot" behavior, you will have to make a copy of the Keys collection and iterate over the copy, otherwise you will iterate over mutable thread safe collection.

like image 21
Volma Avatar answered Oct 22 '22 19:10

Volma