ConcurrentDictionary In C#

Introduction

 
Dear reader, as we all know, data concurrency (fetching data from the database and inserting data into the database) without any duplication is one of the most important parts of software development. If we are not able to solve this problem, then your software is not able to move into the production environment.
 
So I thought I should write an article to give my readers a better idea on how to solve these kind of problems.
 
In this article, we will try to learn when we work on a multi-threaded application, how we can apply the best generic collection which is called ConcurrentDictionary in C#
 
So on the basis of that, I am trying to explain in the following section:
  • Definition
  • Useful Methods
    • TryAdd(TKey, TValue)
    • TryGetValue(TKey, TValue)
    • TryRemove(TKey, TValue)
    • TryUpdate(TKey, TValue, TValue)
    • Clear()
    • ContainsKey(TKey)
    • ToArray
    • ToDictionary
    • ToList

  • Useful Properties

Definition

 
ConcurrentDictionary is a generic collection, ConcurrentDictionary was introduced in .NET framework 4.0 as it is available in System.Collections.Concurrent namespace, this generic collection is used in the case of a multi-threaded application.
 
So in this case, we do not need to use the lock keyword to lock the function.
 
As you know, Microsoft in C# already provided a generic collection that is called Dictionary.
 
So why do we need ConcurrentDictionary in C#?
 
The answer is that ConcurrentDictionary provides a thread-safe functionality.
 
So the question is, what is the actual meaning of thread-safe in C#?  As you know, when we are working on real-time projects, then it is possibile to work on a multi-threaded application to add and get items from the dictionary. the result may be wrong because the Dictionary is not thread-safe.
 
ConcurrentDictionary is, by default, thread-safe, which provides the correct result.
 
Let's see it with a simple example:
  1. public class Program {  
  2.     static Dictionary < string, int > _mydic = new Dictionary < string, int > ();  
  3.     static ConcurrentDictionary < string, int > _mydictConcu = new ConcurrentDictionary < string, int > ();  
  4.     static void Main() {  
  5.         Thread mythread1 = new Thread(new ThreadStart(InsertData));  
  6.         Thread mythread2 = new Thread(new ThreadStart(InsertData));  
  7.         mythread1.Start();  
  8.         mythread2.Start();  
  9.         mythread1.Join();  
  10.         mythread2.Join();  
  11.         Thread mythread11 = new Thread(new ThreadStart(InsertDataConcu));  
  12.         Thread mythread21 = new Thread(new ThreadStart(InsertDataConcu));  
  13.         mythread11.Start();  
  14.         mythread21.Start();  
  15.         mythread11.Join();  
  16.         mythread21.Join();  
  17.         Console.WriteLine($ "Result in Dictionary : {_mydic.Values.Count}");  
  18.         Console.WriteLine("********************************************");  
  19.         Console.WriteLine($ "Result in Concurrent Dictionary : {_mydictConcu.Values.Count}");  
  20.         Console.ReadKey();  
  21.     }  
  22.     static void InsertData() {  
  23.         for (int i = 0; i < 100; i++) {  
  24.             _mydic.Add(Guid.NewGuid().ToString(), i);  
  25.         }  
  26.     }  
  27.     static void InsertDataConcu() {  
  28.         for (int i = 0; i < 100; i++) {  
  29.             _mydictConcu.TryAdd(Guid.NewGuid().ToString(), i);  
  30.         }  
  31.     }  
  32. }  
  33. // Output    
  34. Result in Dictionary: 189 ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** Result in Concurrent Dictionary: 200  
 
As you see in the output, in the case dictionary we are only getting 189, while in ConcurrentDictionary, we are getting 200 as the expected result.
 
So we may or may not get the expected result in the dictionary.
 
I hope the basic understanding is clear now.
 
Let's learn its useful methods:
 
TryAdd(TKey, TValue)
 
This method is used to add the item in ConcurrentDictionary:
  1. static void Main(string[] args) {  
  2.     ConcurrentDictionary < string, string > _myConcuDict = new ConcurrentDictionary < string, string > ();  
  3.     _myConcuDict.TryAdd("1""A");  
  4.     _myConcuDict.TryAdd("2""B");  
  5.     _myConcuDict.TryAdd("3""C");  
  6. }  
This TryAdd method return bool value like this: public bool TryAdd(TKey key, TValue value);
 
So if we add any value and it is added successfully, then it will return true, otherwise, it will return false.
  1. static void Main(string[] args) {  
  2.     ConcurrentDictionary < string, string > _myConcuDict = new ConcurrentDictionary < string, string > ();  
  3.     //returns true    
  4.     bool r1 = _myConcuDict.TryAdd("1""A");  
  5.     //returns true    
  6.     bool r2 = _myConcuDict.TryAdd("2""B");  
  7.     //returns false;    
  8.     bool r3 = _myConcuDict.TryAdd("1""C");  
  9. }  
Here, in this example, r3 is returned as false because the item key already exists in ConcurrentDictionary.
 
TryGetValue(TKey, TValue)
 
In this method, we will get an item by the given key:
  1. static void Main(string[] args) {  
  2.     ConcurrentDictionary < string, string > _myConcuDict = new ConcurrentDictionary < string, string > ();  
  3.     bool firstItem = _myConcuDict.TryAdd("1""A");  
  4.     bool secondItem = _myConcuDict.TryAdd("2""B");  
  5.     string item1;  
  6.     string item2;  
  7.     //returns true    
  8.     bool isItemExists1 = _myConcuDict.TryGetValue("1", out item1);  
  9.     Console.WriteLine(item1); // "A"    
  10.     //returns false    
  11.     bool isItemExists2 = _myConcuDict.TryGetValue("3", out item2);  
  12.     Console.WriteLine(item2); // it will print blank    
  13. }  
Here, in TryGetValue, 3 does not exist in ConcurrentDictionary, so it will print blank.
 
Here, we could also use an iteration to get all the items:
  1. foreach(var item in _myConcuDict) {  
  2.     Console.WriteLine(item.Key + "-" + item.Value);  
  3. }   
TryRemove(TKey, TValue)
 
ConcurrentDictionary also provides the ability to remove a specific item by a given key.
  1. static void Main(string[] args) {  
  2.     ConcurrentDictionary < string, string > _myConcuDict = new ConcurrentDictionary < string, string > ();  
  3.     _myConcuDict.TryAdd("1""A");  
  4.     _myConcuDict.TryAdd("2""B");  
  5.     _myConcuDict.TryAdd("3""C");  
  6.     _myConcuDict.TryAdd("4""D");  
  7.     string itmRemove;  
  8.     bool r1 = dictionary.TryRemove("2", out itmRemove); //Returns true    
  9.     Console.WriteLine(removedItem); // "B"    
  10. }  
In this example, it will remove item 2 given the key.
 
TryUpdate(TKey, TValue, TValue)
 
This method is used to update the item if given a key whose value matches the condition.
  1. static void Main(string[] args) {  
  2.     ConcurrentDictionary < string, string > _myConcuDict = new ConcurrentDictionary < string, string > ();  
  3.     _myConcuDict.TryAdd("1""A");  
  4.     _myConcuDict.TryAdd("2""B");  
  5.     _myConcuDict.TryAdd("3""C");  
  6.     _myConcuDict.TryAdd("4""D");  
  7.     string newItem;  
  8.     bool returnTrue = dictionary.TryUpdate("1""P""A"); //Returns true    
  9.     dictionary.TryGetValue("1", out newItem);  
  10.     Console.WriteLine(newValue); // "P"    
  11.     bool returnsFalse = dictionary.TryUpdate("2""Q""CDD"); //Returns false    
  12.     dictionary.TryGetValue("2", out newItem);  
  13.     //it will return old value like B, Because of given condition is not matched.    
  14.     Console.WriteLine(newValue); //B    
  15. }  
Clear()
 
This method is used to remove all items from ConcurrentDictionary.
  1. _myConcuDict.Clear();   
ContainsKey(TKey)
 
Sometimes, we are required to check if a particular key item exists or not in a given collection.
 
So here, ConcurrentDictionary also provides a method to check it.
  1. //It will return true, because of givent key exist in ConcurrentDictionary  
  2. bool r1 = dictionary.ContainsKey("1");  
  3. //It will return false, because of givent key not exist in ConcurrentDictionary  
  4. bool r2 = dictionary.ContainsKey("5");  
Note
Sometimes we are required to copy only a collection to another collection.
 
For that, ConcurrentDictionary provides various casting methods to do this.
  • ToArray
  • ToDictionary
  • ToList
  1. static void Main(string[] args) {  
  2.     ConcurrentDictionary < string, string > _myConcuDict = new ConcurrentDictionary < string, string > ();  
  3.     _myConcuDict.TryAdd("1""A");  
  4.     _myConcuDict.TryAdd("2""B");  
  5.     _myConcuDict.TryAdd("3""C");  
  6.     _myConcuDict.TryAdd("4""D");  
  7.     // Convert in Array    
  8.     KeyValuePair < string, string > [] keyValues = _myConcuDict.ToArray();  
  9.     // Convert in Dictionary    
  10.     Dictionary < string, string > myDict = _myConcuDict.ToDictionary(w => w.Key, m => m.Value);  
  11.     // Convert in List    
  12.     List < KeyValuePair < string, string >> lst = _myConcuDict.ToList();  
  13. }  
Properties
 
The following properties can be used based on our requirements:
  • Count : This property is use to count the record
  • IsEmpty : This property is use to check ConcurrentDictionary is empty or not
  • var isEmplty = _mydictConcu.IsEmpty
  • Item[TKey]: This property is used to get or set for particular item
  • Keys : This projects is used to get list of items keys in given ConcurrentDictionary
  • Values : This property is used to get the value of given ConcurrentDictionary.

Summary

 
ConcurrentDictionary was introduced in .NET 4.0 and is available in the System.Collections.Concurrent namespace. It is thread-safe and internally uses locking. It is useful in the case of a multi-threaded application. However, ConcurrentDictionary is slower than Dictionary.
 
Thank you for taking your valuable time to read the full article.


Similar Articles