C# Multithreaded Resource access – Concurrent Collections


Concurrent collections are a feature added in .Net 4.0 and allow developers to create thread safe collections without worrying as much as they had to worry with Generic Collections.
They do not remove any possible concern the developer should have while working with resources that are accessed from multiple threads, but removes many of the common ones.

Now we have many powerful collections in .Net and especially Generic ones that are really fast and really convenient, but none of the ones in the System.Collections.Generic namespace are thread-safe.

So what would we do if we wanted to have multi-threaded access to a resource?
Use locks of course.
For example to add a key value pair in a dictionary we would implement a wrapper method like the following

And after this we would hope that nobody violates this, because any other developer could actually mistakenly directly try to access the dictionary and add a key value pair without the use of the above method.

But developers do not rely on hopes and dreams because they always get shattered.
So what would we do? we would create a wrapper class that wraps around the whole dictionary and exposes only thread-safe methods to any possible user. All those thread-safe methods would be implemented using locks. Usually this would be done on a separate dll or namespace to encapsulate the internal collection even better.

In fact Microsoft did something similar when implementing SynchronizedCollection. In fact, SynchronizedCollection is simply a wrapper around List that uses locks whenenever there is need to access the lists data.

But wouldn’t that actually hinder performance and make the parallel version of our code run even slower than the serial one? Wouldn’t that make our threads so slow that they would wait for so long when locking in high parallelization paradigms?

 

YES IT WOULD

So, in comes .Net 4.0 and Microsofts new implementation of concurrent collections, under a new namespace, and with a promise of a combination efficiency and thread-safety.

So what do we have inside this namespace?
1. ConcurrentDictionary
2. ConcurrentStack
3. ConcurrentQueue
4. ConcurrentBag
5. BlockingCollection

So let’s see how to use each one.

ConcurrentCollections

As you see concurrent dictionaries (and in fact all concurrent collections) utilize the TryPattern in which safeguards against failures, returns true or false whether the operation was successful or not and guarantees atomicity of each operation.
What is atomicity? Well we say an operation is atomic when it completes within a single step without being interrupted. Only atomic operations are thread-safe. Code inside a locking block is by definition atomic. Any other code that has no guarantee of atomicity is not in fact atomic (even methods of .Net framework and C# itself).

ConcurrentQueue

Similarly a concurrent queue is the concurrent implementation of queue and it uses the TryPattern.

The following code is using ConcurrentQueue in a multi-threaded operation.

As you can see in the above example I do not always use the try methods. In parts of code where I know that no other thread/task will access the resource I can use the “non-thread-safe” methods instead of the thread-safe version. This should be done only in cases where you are 100% positive that no other task will simultaneously access the collection. So if in doubt, always use the thread-safe methods.
And in this example again you also see that we are using the TryPattern to remove from queue and the return value to see if anything else is left inside the queue.

ConcurrentStack

Similarly a concurrent Stack is a concurrent version of the stack we all know. You can perform multithreaded LIFO access to data with the same pattern.

Again in parts of the code where guaranteed single threaded execution exists, there is no reason to use the multi-threaded option.

ConcurrentBag

The final part of our list of concurrent paradigms that .Net is giving us out of the box is the concurrent bag.
Concurrent bag is used to store unordered items to be processed. In cases that we do not care about the order items come and will be processed but we want to be multi-threaded safe we use concurrent bag.

Two unique methods that come with ConcurrentBag are TryPeek and TryTake
They both adhere to the try pattern.
The differences are that TryPeek tries to get a value and returns whether it was successful or not, while TryTake also removes the value from the Bag.

That’s it for Concurrent Collections in the .Net Framework.
Use them wisely

Leave a Reply