Working with Concurrent Collections in .NET Framework 4.0

Concurrent collections in .NET Framework
4.0 allow the developers to create type safe as well as thread safe
collections. These collection classes form an essential part of the parallel
programming feature and are available under the namespace System.Collections.Concurrent.
Below are the different types of concurrent collections.

1. ConcurrentDictionary<TKey,
TValue>
2. ConcurrentStack<T>
3. ConcurrentQueue<T>
4. ConcurrentBag<T>
5. BlockingCollection<T>

The producer and consumer
pattern can easily be implemented while using ConcurrentStack, ConcurrentQueue
and ConcurrentBag as they implement the interface IProducerConsumerCollection.

Need for Concurrent Collections

Though
System.Collections namespace offers a wide range of collections, the only thing
which limits our use of them in a multi-threaded or parallel environment is
that they are not thread-safe. A non thread-safe collection will lead to
race conditions and throw unexpected exceptions. In order to make them thread
safe, a locking mechanism should be implemented as shown in the example below.

Dictionary<string, string> _dictionaryCollection = new Dictionary<string, string>();
object _lockingObject = new object();
public void AddToCollection(string key, string value)
{
            lock (_lockingObject)
            {
                _dictionaryCollection.Add(key, value);
            }
}
 

The above code will work fine in
a multi-threaded operation but it is a little costly in terms of performance,
since it blocks all the other threads until the current thread exits the lock.
This is why concurrent collections are required. Concurrent collections can be
shared across multiple threads with no explicit locks and it also increases the
scalability and performance of multi-threaded operations.

ConcurrentDictionary

ConcurrentDictionary is used to
create a thread-safe key value pair. It offers several methods, such as TryAdd,
TryGetValue, AddOrUpdate, etc. All these methods are thread-safe. Below is the
sample code for using ConcurrentDictionary.

class Program
{
 
    static void Main(string[] args)
    {
        //Consider this as a list from database which is huge and we need to represent this data as a keyvalue pair using ConcurrentDictionary
        string[] techWebSites = new string[] { "www.codeguru.com", "www.internet.com", "www.devx.com", "www.developer.com" };
 
        Task[] taskList = new Task[2];
 
        ConcurrentDictionary<int, string> dictionaryWebsites = new ConcurrentDictionary<int, string>();
 
        //With the creation of two tasks we have divided the load of populating the collection byt without doing any explicit synchronization
        taskList[0] = Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < techWebSites.Length; i++)
                {
                    if (dictionaryWebsites.TryAdd(i, techWebSites[i]))
                        Console.WriteLine("Website {0} added to the dictionary!", techWebSites[0]);
                }
            });
 
        taskList[1] = Task.Factory.StartNew(() =>
        {
            for (int i = 0; i < techWebSites.Length; i++)
            {
                if (dictionaryWebsites.TryAdd(i, techWebSites[i]))
                    Console.WriteLine("Website {0} added to the dictionary!", techWebSites[0]);
            }
        });
 
        Task.WaitAll();
 
    }
}

ConcurrentQueue

ConcurrentQueue is a thread-safe
first in first out (FIFO) collection. The code below is using ConcurrentQueue
in a multi-threaded operation.

class Program
{
    static void Main(string[] args)
    {
        ConcurrentQueue<string> nameCollection = new ConcurrentQueue<string>();
        nameCollection.Enqueue("Adam Hollioke");
        nameCollection.Enqueue("Ben Hollioke");
        nameCollection.Enqueue("Ronie Irani");
        nameCollection.Enqueue("Patrick");
        nameCollection.Enqueue("Stuart Clarke");
        nameCollection.Enqueue("Mike Gatting");
 
        Task[] taskList = new Task[2];
 
        taskList[0] = Task.Factory.StartNew(() =>
            {
                string name = String.Empty;
                while (nameCollection.TryDequeue(out name))
                    Console.WriteLine("Name {0} is dequeued!", name);
            });
 
        taskList[1] = Task.Factory.StartNew(() =>
        {
            string name = String.Empty;
            while (nameCollection.TryDequeue(out name))
                Console.WriteLine("Name {0} is dequeued!", name);
        });
 
        Task.WaitAll();
        Console.ReadLine();
    }
}

ConcurrentStack

ConcurrentStack is a thread-safe
collection to perform last in first out (LIFO) operations. The code below demonstrates
the use of ConcurrentStack.

class Program
{
    static void Main(string[] args)
    {
        ConcurrentStack<int> numberCollection = new ConcurrentStack<int>();
        for (int i = 10; i <= 20; i++)
        {
            numberCollection.Push(i);
        }
 
        Parallel.For(0, 10, i =>
            {
                int value = 0;
                if (numberCollection.TryPop(out value))
                    Console.WriteLine("Value {0} is popped out!", value);
            });
 
        Console.ReadLine();
    }
}

A range of values can be pushed into
or popped out from the ConcurrentStack using the methods PushRange and
TryPopRange.

ConcurrentBag

ConcurrentBag is used to maintain
the list of unordered items. TryTake and TryPeek methods can be used it take
the item out of the bag and to peek for the value in the bag respectively.
Below is the sample code to use ConcurrentBag.

class Program
{
    static ConcurrentBag<string> _entityCollection = new ConcurrentBag<string>();
    static void Main(string[] args)
    {
        _entityCollection.Add("Company1");
        _entityCollection.Add("Catalog1");
        _entityCollection.Add("Order1");
        _entityCollection.Add("Company2");
        _entityCollection.Add("Catalog2");
        _entityCollection.Add("Order2");
        _entityCollection.Add("Company3");
        _entityCollection.Add("Catalog3");
        _entityCollection.Add("Order3");
 
        ThreadStart threadStart = new ThreadStart(PrintEntities);
        Thread thread1 = new Thread(threadStart);
        thread1.Start();
        Thread thread2 = new Thread(threadStart);
        thread2.Start();
 
        Console.ReadLine();
    }
 
    static void PrintEntities()
    {
        string entity = String.Empty;
        while (_entityCollection.TryPeek(out entity))
        {
            if (_entityCollection.TryTake(out entity))
                Console.WriteLine(entity);
        }
    }
}

BlockingCollection

BlockingCollection is the perfect
candidate for implementing the producer and consumer pattern over a thread-safe
collection. The producer and consumer pattern generally spawns two threads
where one thread adds the data to the collection and at the same time the
collection would be consumed by the other. BlockingCollection takes care of
blocking the threads until a vacant space is available in the collection while
adding and until a data is added to the collection while consuming. I will
cover the BlockingCollection in more detail in a future article.

I hope this article provided
enough insight about concurrent collections in .NET Framework 4.0. Happy
Reading!

More by Author

Must Read