ConcurrentStack is a thread-safe generic collection class introduced in .NET 4.0 framework. It provides a Last-In-First-Out (LIFO) data structure. You can read more about Stack here.
ConcurrentStack is like wrapper around Stack class. Stack class is not thread-safe. ConcurrentStack provides thread-safety. It internally uses locking to synchronize different threads.
Create ConcurrentStack Instance
Below is the syntax of creating ConcurrentStack instance.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
ConcurrentStack has an overloaded constructor which takes an existing collection and initializes ConcurrentStack with that collection. Below is the example.
List<int> ints = new List<int>();
ints.Add(1);
ints.Add(2);
ints.Add(3);
ConcurrentStack<int> stack = new ConcurrentStack<int>(ints);
int count = stack.Count; //Returns 3
Add Items in ConcurrentStack
To add new items, it provides a Push method. This method inserts an object at the top of Stack.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
Add multiple items
To add multiple items into the ConcurrentStack, it provides a PushRange method. This method takes an array as parameter. Below is the example.
int[] ints = { 1, 2, 3 };
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.PushRange(ints);
Count All Items
To count all items, we can use Count property. This property returns int as total items in the stack. Below is the example.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1);
stack.Push(2);
int count = stack.Count; // 2
Retrieve/Remove item
To retrieve items we have two methods.
- TryPeek
- TryPop
TryPeek retrieves the last inserted item but does not remove that item into the list. It returns the retrieved item into the out parameter. It returns true if it successfully retrieve the item otherwise returns false. If we use TryPeek repeatedly it returns the same item again and again. Below is the example.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1);
stack.Push(2);
int item;
bool isSuccess = stack.TryPeek(out item);
Console.WriteLine(isSuccess); //Print True
Console.WriteLine(item); //Print 2
isSuccess = stack.TryPeek(out item);
Console.WriteLine(isSuccess); //Print True
Console.WriteLine(item); //Print 2
TryPop retrieves the last inserted item and also remove that item from the list. It returns the retrieved item into the out parameter. It returns true if it successfully retrieve the item and remove the item from the stack otherwise returns false.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1);
stack.Push(2);
int item;
bool isSuccess = stack.TryPop(out item); //isSuccess = True, item = 2
isSuccess = stack.TryPop(out item); //isSuccess = True, item = 1
Enumerating all items in ConcurrentStack
As ConcurrentStack implements the IEnumerable interface, we can use it in foreach loop. Below is the example.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
foreach(var item in stack)
{
Console.WriteLine(item);
}
//Output:
// 4
// 3
// 2
// 1
Foreach loop does not remove any item in the stack. It returns the items as last-in-first-out manner.
Is ConcurrentStack has any Item
For checking is the stack is empty or not, it provides an IsEmpty property. This property returns True if the stack is empty otherwise returns false.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
bool isEmpty = stack.IsEmpty; //returns True
stack.Push(1);
isEmpty = stack.IsEmpty; //returns False
Copy ConcurrentStack to Array
It provides a ToArray method which copies all its items into an array.
ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
int[] array = stack.ToArray();
foreach(int item in array)
{
Console.WriteLine(item);
}
//Output:
// 4
// 3
// 2
// 1
ConcurrentStack as Producer/Consumer Example
We can use ConcurrentStack in producer consumer scenarios. You can read more about producer consumer problem here.
In this below example, we have taken two threads. First thread produces/generates items and adds it to the Stack. Second Thread retrieves the items.
class Program
{
static ConcurrentStack<int> stack = new ConcurrentStack<int>();
static int maxItems = 10;
static AutoResetEvent autoEvent1 = new AutoResetEvent(false);
static AutoResetEvent autoEvent2 = new AutoResetEvent(false);
static void Main(string[] args)
{
Task t1 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < maxItems; ++i)
{
stack.Push(i);
autoEvent1.Set();
autoEvent2.WaitOne();
}
});
Task t2 = Task.Factory.StartNew(() =>
{
int item;
for (int i = 0; i < maxItems; ++i)
{
autoEvent1.WaitOne();
if (stack.TryPop(out item))
{
Console.WriteLine(item);
}
autoEvent2.Set();
}
});
Task.WaitAll(t1, t2);
autoEvent1.Close();
autoEvent2.Close();
//Output:
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
}
}
We have taken two AutoResetEvents to synchronize between threads.
Final Words
ConcurrentStack is thread-safe collection class. We can enumerate, add and remove items with multiple threads. It internally uses locking for synchronization.