Kako raditi s ConcurrentBag i ConcurrentDictionary u .Net

Istodobne zbirke u .Netu nalaze se unutar prostora imena System.Collections.Concurrent i pružaju implementacije klasa kolekcija bez zaključavanja i niti sigurne u nitima. Sigurne kolekcije niti prvi su put predstavljene u .Net 4, a kolekcije su prvi put predstavljene kao dio .Net Framework 1.0 i bile su dostupne u imenskom prostoru System.Collections.

Možete iskoristiti istodobne kolekcije za rad sa zbirkama bez potrebe za pisanjem bilo kojeg dodatnog koda za sinkronizaciju niti. Možete pogledati moj članak o ConcurrentStack i ConcurrentQueue.

ConcurrentBag

ConcurrentBag pruža nit uređenu kolekciju neuređenog skupa elemenata. Evo popisa važnih metoda klase ConcurrentBag.

  • Dodaj (T element) - Ova se metoda koristi za dodavanje elementa u ConcurrentBag.
  • TryPeek (out T) - Ova metoda koristi se za dohvaćanje elementa iz ConcurrentBag bez uklanjanja.
  • TryTake (out T) - Ova se metoda koristi za dohvaćanje elementa iz ConcurrentBag. Imajte na umu da ovaj način uklanja predmet iz kolekcije.

Sljedeći isječak koda ilustrira kako možete stvoriti ConcurrentBag kolekciju i u nju pohraniti stavke.

ConcurrentBag concurrentBag = new ConcurrentBag();

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

    {

        concurrentBag.Add(i);

    }

Ako biste trebali dohvatiti predmete iz zbirke, trebali biste napisati sljedeći kod:

while (concurrentBag.Count > 0)

  {

      Int32 element;

      if (concurrentBag.TryTake(out element))

       {

         Console.WriteLine(element);

       }

  }

Imajte na umu kako je korištena metoda TryTake: Vraća true u uspjehu, inače false. Metoda TryTake također uklanja predmet iz kolekcije. Petlja while nastavlja izvršenje sve dok broj stavki u zbirci nije veći od nule. Evo kompletnog popisa kodova za vašu referencu.

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();

        }

Istodobni rječnik

Rječnik je generička zbirka parova ključ / vrijednost. Brži je od Hashtablea, jer eliminira režijske troškove boksa i unboxinga. ConcurrentDictionary nalazi se unutar prostora imena System.Collections.Concurrent i predstavlja rječnik zaštićen nitima.

Važni članovi klase ConcurrentDictionary uključuju sljedeće:

  • TryAdd: Ova metoda koristi se za dodavanje stavke u primjeru ConcurrentDictionary. Imajte na umu da ova metoda baca iznimku ako je ključ već prisutan u zbirci.
  • TryGetValue: Ova metoda koristi se za dohvaćanje predmeta iz kolekcije.
  • TryRemove: Ova metoda se koristi za uklanjanje predmeta iz kolekcije.
  • TryUpdate: Ova metoda koristi se za ažuriranje određenog ključa u instanci ConcurrentDictionary novom isporučenom vrijednošću.

Sljedeći isječak koda pokazuje kako možete stvoriti instancu ConcurrentDictionary i dodati joj stavke:

ConcurrentDictionary obj = new ConcurrentDictionary();

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

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

Ako sada pokušate dodati još jednu stavku, ali s istim ključem, to neće uspjeti. Pogledajte isječak koda u nastavku.

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

Vrijednost varijable uspjeha je "false" jer pokušaj dodavanja vrijednosti istim ključem ne uspije.

Sljedeći isječak koda ilustrira kako možete dohvatiti predmet iz zbirke na temelju ključa.

string item = null;

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

Ako biste dohvatili sve stavke u zbirci, umjesto toga biste mogli upotrijebiti sljedeći isječak koda.

foreach(var v in obj)

    {

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

    }

Sljedeći isječak koda pokazuje kako možete ukloniti stavku iz zbirke.

string item = null;

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

Ako biste uklonili sve stavke, umjesto njega mogao bi se koristiti sljedeći isječak koda.

obj.Clear();

Sada razmotrite sljedeće dvije statičke metode.

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);

            }

        }

Evo kako možete izvršiti gornje dvije metode istovremeno na dvije instance Task - jedna za spremanje vrijednosti u zbirku, a druga za čitanje vrijednosti iz zbirke.

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

}

Ako izvršite gornji kôd, iznimka neće biti izbačena jer je ovdje zbirka zaštićena od niti.