Wielowątkowe ConcurrentQueue oraz ConcurrentStack

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.

2 thoughts on “Wielowątkowe ConcurrentQueue oraz ConcurrentStack”

  1. 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?

Leave a Reply

Your email address will not be published.