Comment travailler avec ConcurrentBag et ConcurrentDictionary dans .Net

Les collections simultanées dans .Net sont contenues dans l'espace de noms System.Collections.Concurrent et fournissent des implémentations sans verrouillage et thread-safe des classes de collection. Les collections thread-safe ont été introduites pour la première fois dans .Net 4, et les collections ont été introduites pour la première fois dans le cadre de .Net Framework 1.0 et étaient disponibles dans l'espace de noms System.Collections.

Vous pouvez profiter des collections simultanées pour travailler avec des collections sans avoir à écrire du code supplémentaire pour la synchronisation des threads. Vous pouvez consulter mon article sur ConcurrentStack et ConcurrentQueue.

ConcurrentBag

Le ConcurrentBag fournit une collection thread-safe d'un ensemble d'éléments non ordonnés. Voici la liste des méthodes importantes de la classe ConcurrentBag.

  • Add (élément T) - Cette méthode est utilisée pour ajouter un élément au ConcurrentBag.
  • TryPeek (out T) - Cette méthode est utilisée pour récupérer un élément de ConcurrentBag sans le supprimer.
  • TryTake (out T) - Cette méthode est utilisée pour récupérer un élément de ConcurrentBag. Notez que cette méthode supprime l'élément de la collection.

L'extrait de code suivant illustre comment créer une collection ConcurrentBag et y stocker des éléments.

ConcurrentBag concurrentBag = new ConcurrentBag();

  for (int i = 0; i < 10; i++)

    {

        concurrentBag.Add(i);

    }

Si vous deviez récupérer les éléments de la collection, vous devez écrire le code suivant:

while (concurrentBag.Count > 0)

  {

      Int32 element;

      if (concurrentBag.TryTake(out element))

       {

         Console.WriteLine(element);

       }

  }

Notez comment la méthode TryTake a été utilisée: elle renvoie true en cas de succès, false dans le cas contraire. La méthode TryTake supprime également l'élément de la collection. La boucle while continue son exécution jusqu'à ce que le nombre d'éléments de la collection soit supérieur à zéro. Voici la liste complète des codes pour votre référence.

static void Main(string[] args)

        {

            ConcurrentBag concurrentBag = new ConcurrentBag();

            for (int i = 0; i < 10; i++)

            {

                concurrentBag.Add(i);

            }

            while (concurrentBag.Count > 0)

            {

                Int32 element;

                if (concurrentBag.TryTake(out element))

                {

                    Console.WriteLine(element);

                }

            }

            Console.Read();

        }

ConcurrentDictionary

Un dictionnaire est une collection générique de paires clé / valeur. C'est plus rapide qu'un Hashtable car il élimine les frais généraux de boxe et de déballage. Le ConcurrentDictionary est contenu dans l'espace de noms System.Collections.Concurrent et représente un dictionnaire thread-safe.

Les membres importants de la classe ConcurrentDictionary incluent les éléments suivants:

  • TryAdd: cette méthode est utilisée pour ajouter un élément dans l'instance ConcurrentDictionary. Notez que cette méthode lève une exception si la clé est déjà présente dans la collection.
  • TryGetValue: cette méthode est utilisée pour récupérer un élément de la collection.
  • TryRemove: cette méthode est utilisée pour supprimer un élément de la collection.
  • TryUpdate: cette méthode est utilisée pour mettre à jour une clé particulière dans l'instance ConcurrentDictionary avec la nouvelle valeur fournie.

L'extrait de code suivant montre comment créer une instance ConcurrentDictionary et y ajouter des éléments: The following code snippet shows how you can create a ConcurrentDictionary instance and add items to it:

ConcurrentDictionary obj = new ConcurrentDictionary();

obj.TryAdd("X001", "This is the first value.");

obj.TryAdd("X002", "This is the second value.");

Si vous essayez maintenant d'ajouter un autre élément mais avec la même clé, cela échoue. Reportez-vous à l'extrait de code ci-dessous.

bool success = obj.TryAdd("X002", "This is the third value.");

La valeur de la variable de succès est "false" car la tentative d'ajout d'une valeur avec la même clé échoue.

L'extrait de code suivant illustre comment vous pouvez récupérer un élément de la collection en fonction d'une clé.

string item = null;

bool isExist = obj.TryGetValue("X001", out item);

Si vous deviez récupérer tous les éléments de la collection, vous pouvez utiliser l'extrait de code suivant à la place.

foreach(var v in obj)

    {

        Console.WriteLine(v.Key + "---" + v.Value);

    }

L'extrait de code suivant montre comment vous pouvez supprimer un élément de la collection.

string item = null;

bool result = obj.TryRemove("X001", out item);

Si vous supprimez tous les éléments, l'extrait de code suivant peut être utilisé à la place.

obj.Clear();

Maintenant, considérez les deux méthodes statiques suivantes.

static void FirstTask(ConcurrentDictionary obj)

        {

            for (int i = 0; i < 10; ++i)

            {

                obj.TryAdd(i.ToString(), i.ToString());

                Thread.Sleep(100);

            }

        }

        static void SecondTask(ConcurrentDictionary obj)

        {

            Thread.Sleep(1000);

            foreach (var item in obj)

            {

                Console.WriteLine("Key: "+item.Key + "   Value: " + item.Value);

                Thread.Sleep(100);

            }

        }

Voici comment vous pouvez exécuter les deux méthodes ci-dessus sur deux instances de Task simultanément - l'une pour stocker des valeurs dans la collection et l'autre pour lire les valeurs de la collection.

ConcurrentDictionary obj = new ConcurrentDictionary();

Task firstTask = Task.Run(() => FirstTask(obj));           

Task secondTask = Task.Run(() => SecondTask(obj));           

try

{

  Task.WaitAll(firstTask, secondTask);

}

catch (AggregateException ex)

{

   //Write your own code here to handle exception

}

Si vous exécutez le code ci-dessus, l'exception ne sera pas levée car la collection ici est thread-safe.