14 November 2017

concurrent queuing in .NET

If you have built a queuing functionality in .NET app, it's most likely that you would have used Queue class from System.Collection.Generic namespace. The Queue collection has EnQueue and DeQueue methods. The Enqueue method adds an item to queue & Dequeue method removes the item from the queue.

Issues with Queue class from System.Collections.Generic

However, the Enqueue and Dequeue methods aren't thread-safe. It means in a multi-threaded scenario when one thread sees there is an item in the queue and try to Dequeue it, the item might have been removed by another thread; in such situation, InvalidOperationException will be thrown saying "Queue is empty."

static object syncObject = new object();
        static void Main(string[] args)
        {
            Queue<Message> queue = new Queue<Message>();
            while (true)
            {
                if (queue.Count > 0)
                {
                    lock (syncObject)
                    {
                        try
                        {
                            Message item = queue.Dequeue();
                            
                            // Logic to process the item
                            // goes here.
                        }
                        catch (InvalidOperationException ex)
                        {

                        }
                    }
                }
            }
        }

Solution using ConcurrentQueue from System.Collections.Concurrent namespace

To avoid getting such an invalid operation exception on trying to remove a nonexisting item in the queue, you can opt for ConcurrentQueue from the System.Collections.Concurrent namespace. Concurrent Collection were newly added in .NET 4.0 framework.

The ConcurrentQueue class has TryDequeue() method, which will not throw an exception when queue is empty, instead, the method will return false.

static void Main(string[] args)
   {
   ConcurrentQueue<Message> queue = new ConcurrentQueue<Message>();
            while (true)
            {
                Message item = null;
                if (queue.TryDequeue(out item))
                {

                    // Logic to process the item
                    // goes here.
                }

            }
   }

Advantage of using ConcurrentQueue over Queue

  • ConcurrentQueue is from System.Collection.Concurrent, so its thread safe.
  • ConcurrentQueue has TryDequeue() method which will return false when collection is empty and doesnt throw exception. As you know Exceptions are costlier, hence code with ConcurrentQueue will perform better compared to Queue in multithreaded scenario.
  • Without the try-catch block around TryDeque() method, the code logic with ConcurrentQueue is simple and easy to read.

Conclusion

If you are on .NET 4.0, it always better to use ConcurrentQueue compared to Queue. This is because ConcurrenrQueue will perform better in multithread envrionment. Nowadays its quite hard to assume an app which are not multi-threaded, so you can always use ConcurrentQueue going forward.

No comments:

Post a Comment