How to work with ConcurrentBag and ConcurrentDictionary in .Net

The ConcurrentDictionary and ConcurrentBag classes provide lock-free, thread-safe implementations to work with collections in .Net

concurrentcollections
Joydip Kanjilal

Concurrent collections in .Net are contained inside the System.Collections.Concurrent namespace and provide lock-free and thread-safe implementations of the collection classes. Thread safe collections were first introduced in .Net 4, and collections were first introduced as part of .Net Framework 1.0 and were available in the System.Collections namespace.

You can take advantage of the concurrent collections to work with collections sans the need of having to write any extra code for thread synchronization. You can take a look my article on ConcurrentStack and ConcurrentQueue.

ConcurrentBag

The ConcurrentBag provides a thread-safe collection of an unordered set of elements. Here's the list of the important methods of the ConcurrentBag class.

  • Add(T element) - This method is used to add an element to the ConcurrentBag<T>.
  • TryPeek(out T) - This method is used to retrieve an element from ConcurrentBag<T> without removing it.
  • TryTake(out T) - This method is used to retrieve an element from ConcurrentBag<T>. Note that this method removes the item from the collection.

The following code snippet illustrates how you can create a ConcurrentBag collection and store items into it.

ConcurrentBag<Int32> concurrentBag = new ConcurrentBag<Int32>();

  for (int i = 0; i < 10; i++)

    {

        concurrentBag.Add(i);

    }

If you were to retrieve the items in the collection, you should write the following code:

while (concurrentBag.Count > 0)

  {

      Int32 element;

      if (concurrentBag.TryTake(out element))

       {

         Console.WriteLine(element);

       }

  }

Note how the TryTake method has been used: It returns true on success, false otherwise. The TryTake method also removes the item from the collection. The while loop continues execution until the time the count of items in the collection is greater than zero. Here’s the complete code listing for your reference.

static void Main(string[] args)

        {

            ConcurrentBag<Int32> concurrentBag = new ConcurrentBag<Int32>();

            for (int i = 0; i < 10; i++)

            {

                concurrentBag.Add(i);

            }

            while (concurrentBag.Count > 0)

            {

                Int32 element;

                if (concurrentBag.TryTake(out element))

                {

                    Console.WriteLine(element);

                }

            }

            Console.Read();

        }

ConcurrentDictionary

A Dictionary is a generic collection of key/value pairs. It's faster than a Hashtable as it eliminates the boxing and un-boxing overheads. The ConcurrentDictionary is contained inside the System.Collections.Concurrent namespace and represents a thread-safe dictionary.

The important members of the ConcurrentDictionary class include the following:

  • TryAdd: This method is used to add an item in the ConcurrentDictionary instance. Note that this method throws an exception if the key is already present in the collection.
  • TryGetValue: This method is used to retrieve an item from the collection.
  • TryRemove: This method is used to remove an item from the collection.
  • TryUpdate: This method is used to update a particular key in the ConcurrentDictionary instance with the new value supplied.

The following code snippet shows how you can create a ConcurrentDictionary instance and add items to it:

ConcurrentDictionary<string, string> obj = new ConcurrentDictionary<string, string>();

obj.TryAdd("X001", "This is the first value.");

obj.TryAdd("X002", "This is the second value.");

If you now try to add another item but with the same key, it fails. Refer to the code snippet below.

bool success = obj.TryAdd("X002", "This is the third value.");

The value of the success variable is "false" as the attempt to add a value with the same key fails.

The following code snippet illustrates how you can retrieve an item from the collection based on a key.

string item = null;

bool isExist = obj.TryGetValue("X001", out item);

If you were to retrieve all items in the collection, you could use the following code snippet instead.

foreach(var v in obj)

    {

        Console.WriteLine(v.Key + "---" + v.Value);

    }

The following code snippet shows how you can remove an item from the collection.

string item = null;

bool result = obj.TryRemove("X001", out item);

If you were to remove all items, the following code snippet could be used instead.

obj.Clear();

Now, consider the following two static methods.

static void FirstTask(ConcurrentDictionary<string, string> obj)

        {

            for (int i = 0; i < 10; ++i)

            {

                obj.TryAdd(i.ToString(), i.ToString());

                Thread.Sleep(100);

            }

        }

        static void SecondTask(ConcurrentDictionary<string, string> obj)

        {

            Thread.Sleep(1000);

            foreach (var item in obj)

            {

                Console.WriteLine("Key: "+item.Key + "   Value: " + item.Value);

                Thread.Sleep(100);

            }

        }

Here's how you can execute the above two methods on two Task instances simultaneously -- one to store values into the collection and the other to read values from the collection.

ConcurrentDictionary<string, string> obj = new ConcurrentDictionary<string, string>();

Task firstTask = Task.Run(() => FirstTask(obj));           

Task secondTask = Task.Run(() => SecondTask(obj));           

try

{

  Task.WaitAll(firstTask, secondTask);

}

catch (AggregateException ex)

{

   //Write your own code here to handle exception

}

If you execute the above code, exception will not be thrown as the collection here is thread-safe.

Copyright © 2017 IDG Communications, Inc.