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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With