Introduction
The ConcurrentStack<T>
class in C# is part of the System.Collections.Concurrent
namespace and represents a thread-safe, last-in, first-out (LIFO) collection of objects. It is particularly useful in scenarios where multiple threads are adding and removing items from a collection simultaneously, and you need a stack-like behavior. The ConcurrentStack<T>
is designed to be thread-safe and optimized for high concurrency.
Key Features of ConcurrentStack
- Thread-Safe: Supports concurrent push and pop operations.
- LIFO Order: Maintains elements in a last-in, first-out order.
- High Throughput: Optimized for scenarios with high contention.
- No Locking: Uses lightweight synchronization mechanisms to avoid locks.
Creating a ConcurrentStack
Declaration and Initialization
You can declare and initialize a ConcurrentStack<T>
in several ways:
Example
using System;
using System.Collections.Concurrent;
namespace ConcurrentStackExample
{
class Program
{
static void Main(string[] args)
{
// Creating an empty ConcurrentStack
ConcurrentStack<int> stack = new ConcurrentStack<int>();
// Adding elements to the ConcurrentStack
stack.Push(1);
stack.Push(2);
stack.Push(3);
// Displaying elements
Console.WriteLine("Elements in ConcurrentStack:");
foreach (int item in stack)
{
Console.WriteLine(item);
}
}
}
}
Output
Elements in ConcurrentStack:
3
2
1
Common Operations on ConcurrentStack
Adding Elements
- Push: Adds an element to the top of the
ConcurrentStack
.
stack.Push(4);
- PushRange: Adds multiple elements to the
ConcurrentStack
.
stack.PushRange(new int[] { 5, 6, 7 });
Removing Elements
- TryPop: Attempts to remove and return an object from the top of the
ConcurrentStack
. Returnstrue
if an object was removed successfully; otherwise,false
.
bool success = stack.TryPop(out int item);
if (success)
{
Console.WriteLine($"Popped: {item}");
}
else
{
Console.WriteLine("No items to pop.");
}
- TryPopRange: Attempts to pop and return multiple objects from the
ConcurrentStack
. Returns the number of objects successfully popped.
int[] items = new int[3];
int count = stack.TryPopRange(items);
Console.WriteLine($"Popped {count} items.");
Peeking at Elements
- TryPeek: Attempts to return an object from the top of the
ConcurrentStack
without removing it. Returnstrue
if an object was returned successfully; otherwise,false
.
bool success = stack.TryPeek(out int item);
if (success)
{
Console.WriteLine($"Peeked: {item}");
}
else
{
Console.WriteLine("No items to peek.");
}
Checking the Collection Size
- Count: Gets the number of elements in the
ConcurrentStack
.
int count = stack.Count;
Console.WriteLine($"Count: {count}");
Iterating Through a ConcurrentStack
You can iterate through a ConcurrentStack
using a foreach
loop. However, note that the collection is thread-safe for concurrent modifications, and the snapshot taken by foreach
might not reflect all concurrent changes.
Example
foreach (int item in stack)
{
Console.WriteLine(item);
}
Practical Example
Let’s create a practical example where we use a ConcurrentStack<T>
to manage a collection of tasks executed by multiple threads, allowing them to add completed tasks to the stack.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace TaskCompletionExample
{
class Program
{
static void Main(string[] args)
{
ConcurrentStack<string> completedTasks = new ConcurrentStack<string>();
// Creating and starting multiple tasks
Task[] tasks = new Task[5];
for (int i = 0; i < tasks.Length; i++)
{
int taskId = i;
tasks[i] = Task.Run(() => CompleteTask(completedTasks, taskId));
}
// Waiting for all tasks to complete
Task.WaitAll(tasks);
// Displaying completed tasks
Console.WriteLine("Completed Tasks:");
foreach (string task in completedTasks)
{
Console.WriteLine(task);
}
}
static void CompleteTask(ConcurrentStack<string> completedTasks, int taskId)
{
// Simulate some work
Task.Delay(1000).Wait();
string taskName = $"Task {taskId}";
completedTasks.Push(taskName);
Console.WriteLine($"{taskName} completed.");
}
}
}
Output
Task 0 completed.
Task 1 completed.
Task 2 completed.
Task 3 completed.
Task 4 completed.
Completed Tasks:
Task 4
Task 3
Task 2
Task 1
Task 0
Conclusion
The ConcurrentStack<T>
class in C# provides a thread-safe, last-in, first-out (LIFO) collection of objects that is optimized for high concurrency. It supports concurrent push and pop operations without locking, making it ideal for scenarios where multiple threads need to interact with a shared stack. Understanding how to use ConcurrentStack<T>
effectively can help you manage collections of data in multi-threaded applications.