Working with Concurrent Queue in C#

A queue is a data structure that works on a First – In – First – Out (FIFO) basis. So, you would typically use a queue when you need items to be added and removed on a FIFO basis. Items are added at the end of the collection – this is also known as enqueue. On the contrary, items are deleted from the beginning of the queue — this process is known as dequeue or dequeuing of items. However, the Queue class provides you a blocking collection. If you need a queue that is synchronized and thread safe, you may want to use the ConcurrentQueue class instead. This article discusses how we can use the ConcurrentQueue class in C#.

When do we need to use a thread safe collection anyway?

You have both generic and non-generic versions of the queue class in .NET. However, the Queue class is not thread-safe and you might have synchronization issues when working with queues in a multithreaded environment. Usage of the concurrent queue will enable your application to allow multiple threads to read or write to the queue without the need of an explicit lock — synchronization will be taken care of by the concurrent queue. The ConcurrentQueue represents a lock-free, thread-safe version of the Queue data structure in .NET.

Programming ConcurrentQueue in C#

The System.Collectons.Concurrent namespace contains a collection of types that are thread-safe. So, in using such types, you need not bother about thread safety when adding or removing items from these collections. The System.Collections.Concurrent contains the ConcurrentQueue class that can be used to add and remove items from a concurrent queue sans the need of any user defined thread safety in the code. The System.Collections.Concurrent namespace contains many types such as, BlockingCollection<T>, ConcurrentDictionary<TKey, TValue>, Concurremtqueue, ConcurrentStack<T>, ConcurrentBag<T> and IProducerConsumerCollection<T>.

The MSDN states: “The ConcurrentQueue<T> and ConcurrentStack<T> classes do not use locks at all. Instead, they rely on Interlocked operations to achieve thread safety.” Reference: https://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx.

There are two main methods that you would frequently take advantage of while working with a ConcurrentQueue. These include: Enqueue and TryDequeue. The following code snippet shows how you can use the Enqueue() method of the ConcurrentQueue class to insert items in a concurrent queue.

var concurrentQueue = new ConcurrentQueue<string>();
for(int i = 0; i < 10; i++)
concurrentQueue.Enqueue(i.ToString());

To dequeue items from a concurrent queue you can take advantage of the TryDequeue method. The TryDequeue method accepts an out parameter of a generic type and returns a boolean value depending on whether the item was dequeued from the concurrent queue successfully. The following code snippet illustrates how the TryDequeue method can be used to dequeue items from the concurrent queue we created earlier.

string number;
while(concurrentQueue.TryDequeue(out number))
  {
     Console.WriteLine(number);
  }

Once the items in a concurrent queue have been dequeued, you can check if there any items in it using the IsEmpty property.

bool isEmpty = concurrentQueue.IsEmpty; 

In this example, the IsEmpty property will have a value “false” as all the items in the concurrent queue have been dequeued using the while loop given earlier.

Here’s the complete code listing for your reference.

static void Main(string[] args)
        {
            var concurrentQueue = new ConcurrentQueue<string>();
            for (int i = 0; i < 10; i++)
                concurrentQueue.Enqueue(i.ToString());
            if (!concurrentQueue.IsEmpty)
            {
                string number;
                while (concurrentQueue.TryDequeue(out number))
                {
                    Console.WriteLine(number);
                }
            }
            bool isEmpty = concurrentQueue.IsEmpty;
            Console.Read();
        }

The TryPeek method returns an object from the beginning of the ConcurrentQueue without removing the item. So, after you invoke the TryPeek method, the Count property of it will still give you the total number of items present in it since it would remain the same. The following code listing illustrates this.

static void Main(string[] args)
        {
            var concurrentQueue = new ConcurrentQueue<string>();
            for (int i = 0; i < 10; i++)
                concurrentQueue.Enqueue(i.ToString());
            Console.WriteLine("Total number of items in the concurrent queue initially: " + concurrentQueue.Count);
            if (!concurrentQueue.IsEmpty)
            {
                string number;
                if (concurrentQueue.TryPeek(out number))
                {
                    Console.WriteLine("The object at the beginning of the concurrent queue has a value of: "+number);
                }
            }
            Console.WriteLine("Total number of items in the concurrent queue after executing the TryPeek method: " + concurrentQueue.Count);
            Console.Read();
        }

Summary

In this article we have explored the ConcurrentQueue class in C# and how it can be used to implement collections that are thread safe without having to explicitly write code for thread safety.

Source: DevX