Dziś kolejny post o kolekcjach, które mogą być używanie swobodnie w środowisku współbieżnym. Tak jak ConcurrentBag, obiekty przedstawione w tym poście również są mocno zoptymalizowane. ConcurrentQueue to po prostu kolejka. Przykład:
ConcurrentQueue<int> queue = new ConcurrentQueue<int>(); queue.Enqueue(5); queue.Enqueue(6); int result; if(queue.TryDequeue(out result)) { Console.WriteLine(result); } if (queue.TryPeek(out result)) { Console.WriteLine(result); }
Kolejka posiada 3 najważniejsze metody. Dodawanie nowych elementów odbywa się za pomocą Enqueue. TryPeek zwraca element bez jego usunięcia z kolei TryDequeue ma podobną strukturę i zachowanie z tym, że usuwa element z kolejki. Jak widać na przykładzie, sam sposób użycia jest analogiczny do zwykłej kolejki.
Korzystanie ze stosu nie powinno stwarzać problemów:
ConcurrentStack<int> stack = new ConcurrentStack<int>(); stack.Push(5); stack.Push(6); int result; if(stack.TryPeek(out result)) { Console.WriteLine(result); } if (stack.TryPop(out result)) { Console.WriteLine(result); }
Ponadto można zwrócić ze stosu kilka elementów za pomocą tablicy i metody TryPopRange:
ConcurrentStack<int> stack = new ConcurrentStack<int>(); stack.Push(5); stack.Push(6); int[] elements=new int[2]; stack.TryPopRange(elements); Console.WriteLine(elements[0]); Console.WriteLine(elements[1]);
Analogicznie można dodać kilka elementów naraz:
int[] elements=new int[2]; stack.PushRange(elements);
Bardzo dobrą informacją o przedstawionych wyżej 2 kolekcjach jest fakt, że nie używają one blokad ani nawet spinning’u. Wszystko odbywa się za pomocą Interlocked – klasy dostarczającej operacje atomowe.
Podobnie jak w przypadku ConcurrentBag do dyspozycji mamy właściwości Count, IsEmpty oraz metodę Clear. Myślę, że nazwy mówią same za siebie. Należy jednak pamiętać, że w środowisku współbieżnym nie ma sensu porównywać Count z liczbą, ponieważ jest to operacja nieatomowa. Podobnie z IsEmpty – nie ma pewności, że za chwile stos nie będzie pusty. Obie kolekcje wspierają foreach, z tym, że jest to tylko snapshot danych. Jeśli w momencie wykonywania pętli stos lub kolejka zostaną zmodyfikowane, nie zostanie to odzwierciedlone.
Witam,
czy w przykładzie stosu TryPopRange nie wkradł się przypadkiem błąd?
ConcurrentStack stack = new ConcurrentStack();
queue.Push(5);
queue.Push(6);
Zamiast queue.Push powinno być stack.Push?
Tak, oczywiscie, masz racje.
Dzieki za uwage.