Thread Safe List in C#
-
Thread Safe List With the
ConcurrentBag
Class in C# -
Thread Safe List With the
ConcurrentQueue
Class in C# -
Thread Safe List With the
SynchronizedCollection
Class in C# -
Thread Safe List With the
ConcurrentStack
Class in C# - Conclusion
Navigating the intricate realm of concurrent programming in C# demands a robust understanding of thread safety. As multiple threads collaborate to execute tasks simultaneously, the risk of conflicts in shared data structures, such as lists, becomes a central concern.
Without proper synchronization, unpredictable behavior and data corruption may ensue. In this comprehensive exploration, we delve into five key thread-safe mechanisms.
Thread Safe List With the ConcurrentBag
Class in C#
The ConcurrentBag
class is used to create a thread-safe unordered collection of data in C#. The ConcurrentBag
class is very similar to the List
in C# and can be used as a thread-safe list in C#.
To use the ConcurrentBag
class, we have to import the System.Collections.Concurrent
namespace in our project. Multiple threads can access a ConcurrentBag
object simultaneously, but the contents inside the ConcurrentBag
object can only be modified synchronously.
It makes it both usable with multiple threads for concurrent operations and safe from accidental data loss.
See the following example.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class ThreadSafeListExample {
static ConcurrentBag<int> threadSafeBag = new ConcurrentBag<int>();
static void Main() {
Task task1 = Task.Run(() => AddItemsToBag(1, 5));
Task task2 = Task.Run(() => AddItemsToBag(6, 10));
Task.WaitAll(task1, task2);
Console.WriteLine("Thread-Safe Bag Contents:");
foreach (var item in threadSafeBag) {
Console.Write(item + " ");
}
}
static void AddItemsToBag(int start, int end) {
for (int i = start; i <= end; i++) {
threadSafeBag.Add(i);
}
}
}
We commence by declaring a ConcurrentBag<int>
named threadSafeBag
. The ConcurrentBag
is specifically designed to handle concurrent operations without requiring explicit locks, making it an ideal choice for scenarios involving multiple threads.
Moving to the Main
method, we initiate two parallel tasks using Task.Run
. These tasks represent concurrent operations that add items to our threadSafeBag
.
The Task.WaitAll
ensures that the program waits for both tasks to complete before proceeding to the next steps.
The real magic happens in the AddItemsToBag
method. Here, we iterate through a range of integers specified by the start
and end
parameters.
Within the loop, each integer is added to the ConcurrentBag
using the Add
method. It’s crucial to note that under the hood, ConcurrentBag
ensures thread safety without requiring explicit locks.
Output:
Thread-Safe Bag Contents:
10 9 8 7 6 5 4 3 2 1
Thread Safe List With the ConcurrentQueue
Class in C#
The ConcurrentQueue
class is used to create a thread-safe queue data structure in C#. The ConcurrentQueue
works on the principle of first in, first out, just like the List
in C#.
The ConcurrentQueue
object can be used instead of the List
object to create a thread-safe data structure.
See the following example.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class ThreadSafeListExample {
static ConcurrentQueue<int> threadSafeQueue = new ConcurrentQueue<int>();
static void Main() {
Task task1 = Task.Run(() => EnqueueItems(1, 5));
Task task2 = Task.Run(() => EnqueueItems(6, 10));
Task.WaitAll(task1, task2);
Console.WriteLine("Thread-Safe Queue Contents:");
while (threadSafeQueue.TryDequeue(out var item)) {
Console.Write(item + " ");
}
}
static void EnqueueItems(int start, int end) {
for (int i = start; i <= end; i++) {
threadSafeQueue.Enqueue(i);
}
}
}
In the above code, we begin with the declaration of a ConcurrentQueue<int>
named threadSafeQueue
. This class is specifically crafted for scenarios where multiple threads need to interact with a shared queue without the need for explicit locking mechanisms.
Entering the Main
method, we initiate two parallel tasks using Task.Run
. These tasks simulate concurrent operations that enqueue items into our threadSafeQueue
.
The Task.WaitAll
ensures that the program waits for both tasks to complete before progressing.
The heart of our example lies in the EnqueueItems
method. Here, we iterate through a range of integers specified by the start
and end
parameters.
Within the loop, each integer is enqueued into the ConcurrentQueue
using the Enqueue
method. Notably, ConcurrentQueue
handles thread safety internally, alleviating the need for explicit locks.
As we move to the output section, we display the contents of our thread-safe queue using a while
loop with TryDequeue
. This method ensures that elements are dequeued safely, and the loop continues until the queue is empty.
Output:
Thread-Safe Queue Contents:
1 2 3 4 5 6 7 8 9 10
Thread Safe List With the SynchronizedCollection
Class in C#
The SynchronizedCollection
class is used to create a thread-safe collection of objects of some specified type in C#. The SynchronizedCollection
data structure is very similar to the List
data structure in C#.
Both data structures work on the principle of first in, first out. The function to add a new element in both the SynchronizedCollection
and the List
data structures is Add()
.
See the following example.
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
class ThreadSafeListExample {
static Collection<int> synchronizedCollection = new Collection<int>();
static object lockObject = new object();
static void Main() {
Task task1 = Task.Run(() => AddItems(1, 5));
Task task2 = Task.Run(() => AddItems(6, 10));
Task.WaitAll(task1, task2);
Console.WriteLine("Thread-Safe Collection Contents:");
foreach (var item in synchronizedCollection) {
Console.Write(item + " ");
}
}
static void AddItems(int start, int end) {
for (int i = start; i <= end; i++) {
lock (lockObject) {
synchronizedCollection.Add(i);
}
}
}
}
Our journey begins with the creation of a Collection<int>
named synchronizedCollection
. This collection will be the foundation for our thread-safe operations, and the SynchronizedCollection
wrapper will ensure that access to it is synchronized across multiple threads.
On to the Main
method, two parallel tasks are initiated using Task.Run
. These tasks simulate concurrent operations that add items to our synchronizedCollection
.
The Task.WaitAll
ensures that the program waits for both tasks to complete before progressing.
Now, let’s delve into the heart of our example - the AddItems
method. This method encapsulates the logic for adding a range of integers to the synchronized collection.
Inside the loop, each integer is added within a lock
block, specifically locking on the lockObject
. This ensures that only one thread at a time can access and modify the collection, achieving the coveted thread safety.
As we approach the output section, the contents of our thread-safe collection are displayed using a foreach
loop. The synchronization mechanisms provided by SynchronizedCollection
and the lock
statement ensure that the iteration occurs safely, even in a multithreaded environment.
Output:
Thread-Safe Collection Contents:
1 2 3 4 5 6 7 8 9 10
Thread Safe List With the ConcurrentStack
Class in C#
In C#, one powerful tool for achieving thread safety in such scenarios is the ConcurrentStack
class. This class provides a thread-safe collection, ensuring seamless operations in multithreaded environments.
In this exploration, we’ll unravel the significance of thread safety and guide you through a practical example of implementing a thread-safe list using the ConcurrentStack
method.
See the following example.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class ThreadSafeListExample {
static ConcurrentStack<int> threadSafeStack = new ConcurrentStack<int>();
static void Main() {
Task task1 = Task.Run(() => PushItemsToStack(1, 5));
Task task2 = Task.Run(() => PushItemsToStack(6, 10));
Task.WaitAll(task1, task2);
Console.WriteLine("Thread-Safe Stack Contents:");
while (threadSafeStack.TryPop(out var item)) {
Console.Write(item + " ");
}
}
static void PushItemsToStack(int start, int end) {
for (int i = start; i <= end; i++) {
threadSafeStack.Push(i);
}
}
}
We start by declaring a ConcurrentStack<int>
named threadSafeStack
. This collection is engineered to handle concurrent push and pop operations without the need for explicit locks, making it an optimal choice for multithreaded scenarios.
Transitioning to the Main
method, we initiate two parallel tasks using Task.Run
. These tasks simulate concurrent operations that push items onto our threadSafeStack
.
The Task.WaitAll
ensures the program waits for both tasks to complete before proceeding.
The core of our example resides in the PushItemsToStack
method. Inside the loop, a range of integers, specified by the start
and end
parameters, is pushed onto the ConcurrentStack
using the Push
method.
Notably, ConcurrentStack
internally manages thread safety, eliminating the need for explicit locks.
As we navigate to the output section, the contents of our thread-safe stack are displayed using a while
loop with TryPop
. This method ensures safe popping of elements, and the loop continues until the stack is empty.
Output:
Thread-Safe Stack Contents:
10 9 8 7 6 5 4 3 2 1
Conclusion
The discussed methods—ConcurrentBag
, ConcurrentQueue
, SynchronizedCollection
, and ConcurrentStack
—serve as invaluable assets in the dynamic landscape of concurrent programming. Seamlessly integrating these mechanisms empowers developers to tackle the challenges of managing shared resources across multiple threads.
Through elegant solutions and streamlined approaches, these methods contribute to the creation of scalable, resilient, multithreaded applications. As we navigate the ever-evolving demands of parallel execution, the importance of thread safety remains pivotal, and these tools stand as reliable companions in this journey.
Maisam is a highly skilled and motivated Data Scientist. He has over 4 years of experience with Python programming language. He loves solving complex problems and sharing his results on the internet.
LinkedInRelated Article - Csharp Thread
- Thread.Sleep() in C#
- How to Stop a Thread in C#
- How to Wait for a Thread to Finish in C#
- Thread vs Task in C#
- lock Statement in C#