Home > Net >  Is it possible to miss an initial item when enumerating ConcurrentDictionary?
Is it possible to miss an initial item when enumerating ConcurrentDictionary?

Time:10-04

I'm enumerating ConcurrentDictionary, I need to be sure I don't miss any initial item. In other words, I need to be sure I enumerate all initial items.

Initial items: all items in dictionary when the enumeration starts.

The documentation says:

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

But it is not clear if all initial items are enumerated. So I tested it with the following code:

public class Program
{
    static volatile bool enumeratioCompleted;
    static volatile bool enumerationStarted;
    static int itemsAddedInWorkerThread;
    static ConcurrentDictionary<Guid, object> concurrentDic = new ConcurrentDictionary<Guid, object>();

    public static void Main(string[] args)
    {
        var dic = new Dictionary<Guid, object>();
        const int initialItems = 100_000;
        const int workerThreadCount = 4;

        for (int i = 1; i < initialItems; i  )
        {
            var key = Guid.NewGuid();
            var value = new object();
            dic.Add(key, value);
            concurrentDic.TryAdd(key, value);
        }

        var workerThreads = new Thread[workerThreadCount];
        for (var i = 0; i < workerThreadCount; i  )
        {
            workerThreads[i] = new Thread(AddItemsToConcurrentDicWhileEnumerating);
            workerThreads[i].Start();
        }
        int enumeratedItems = 0;
        foreach (var kv in concurrentDic)
        {
            if (enumerationStarted == false) enumerationStarted = true;
            enumeratedItems  ;
            dic.Remove(kv.Key);
        }
        enumeratioCompleted = true;
        for (var i= 0; i < workerThreadCount; i  )
        {
            workerThreads[i].Join();
        }
        Console.WriteLine($"Initial items {initialItems}");
        Console.WriteLine($"Initial items not enumerated: {dic.Count}");
        Console.WriteLine($"Items enumerated: {enumeratedItems}");
        Console.WriteLine($"Items added in worker thread: {itemsAddedInWorkerThread}");
    }

    static void AddItemsToConcurrentDicWhileEnumerating()
    {
        while (enumerationStarted == false) ;
        while (enumeratioCompleted == false)
        {
            var key = Guid.NewGuid();
            var value = new object();
            concurrentDic.TryAdd(key, value);
            Interlocked.Increment(ref itemsAddedInWorkerThread);
        }
    }
}

It outputs something like the following:

Initial items 100000
Initial items not enumerated: 0
Items enumerated: 108301
Items added in worker thread: 136729

So it seems that all initial items are enumerated. Please, could you confirm if it is guaranteed or not?

CodePudding user response:

No, Microsoft makes no other guarantees regarding the enumeration of the ConcurrentDictionary<K,V>, other than it's "safe". That's it. This collection could (theoretically) always return an empty sequence, and still comply with the current state of the specification. For insight, you could check out enter image description here

  • Related