Thread-Safe Collections in C# β Keep Your Data Safe!
Hey, C# champ! π Have you ever faced issues when multiple threads try to modify a collection at the same time?
β Boom! Your program crashes! π±
Well, donβt worry! In this lesson, weβll learn about Thread-safe collections in C# and how they save us from data corruption and race conditions. Let’s make our code safe and smooth! π‘
π What You Are Going to Learn in This Lesson?
βοΈ What are Thread-safe collections in C#?
βοΈ Why do we need them in multithreading?
βοΈ How to use ConcurrentDictionary
, ConcurrentQueue
, ConcurrentStack
, and ConcurrentBag
?
βοΈ Real-world scenarios where they shine!
βοΈ Complete code examples to understand everything clearly!
π€ Why Do We Need Thread-Safe Collections?
Imagine this:
- You are building a banking system.
- Multiple users are transferring money at the same time.
- If two users modify the same list of transactions at once, π₯ data corruption happens!
Β
π‘ Thread-safe collections in C# solve this problem! They allow multiple threads to read and write safely without crashing your program.
π What are Thread-Safe Collections in C#?
Thread-safe collections are special collections designed for safe access in multithreading.
π‘ Instead of using locks, these collections handle concurrent operations efficiently!
π₯ Popular Thread-Safe Collections in C#
ConcurrentDictionary<TKey, TValue>
β Thread-safe key-value storage.ConcurrentQueue<T>
β A thread-safe queue (FIFO).ConcurrentStack<T>
β A thread-safe stack (LIFO).ConcurrentBag<T>
β A collection optimized for unordered operations.
π Example 1: Using ConcurrentDictionary in Threads
ConcurrentDictionary
allows multiple threads to update key-value pairs safely.
β Code Example
using System;
using System.Collections.Concurrent;
using System.Threading;
class Program
{
static ConcurrentDictionary<int, string> data = new ConcurrentDictionary<int, string>();
static void AddData(int key, string value)
{
data.TryAdd(key, value);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} added: {key} -> {value}");
}
static void Main()
{
for (int i = 1; i <= 5; i++)
{
int key = i;
new Thread(() => AddData(key, $"Value{key}")).Start();
}
Thread.Sleep(1000);
Console.WriteLine("\nFinal Dictionary:");
foreach (var pair in data)
{
Console.WriteLine($"{pair.Key} -> {pair.Value}");
}
}
}
β Expected Output
Thread 3 added: 1 -> Value1
Thread 4 added: 2 -> Value2
Thread 5 added: 3 -> Value3
Thread 6 added: 4 -> Value4
Thread 7 added: 5 -> Value5
Final Dictionary:
1 -> Value1
2 -> Value2
3 -> Value3
4 -> Value4
5 -> Value5
β‘οΈ Multiple threads safely add data to the dictionary!
π Example 2: Using ConcurrentQueue in Threads
ConcurrentQueue<T>
is a FIFO (First In, First Out) collection.
β Code Example
using System;
using System.Collections.Concurrent;
using System.Threading;
class Program
{
static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
static void EnqueueData(int value)
{
queue.Enqueue(value);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} enqueued: {value}");
}
static void DequeueData()
{
if (queue.TryDequeue(out int result))
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} dequeued: {result}");
}
}
static void Main()
{
for (int i = 1; i <= 5; i++)
{
int value = i;
new Thread(() => EnqueueData(value)).Start();
}
Thread.Sleep(1000);
new Thread(DequeueData).Start();
new Thread(DequeueData).Start();
}
}
β Expected Output
Thread 3 enqueued: 1
Thread 4 enqueued: 2
Thread 5 enqueued: 3
Thread 6 enqueued: 4
Thread 7 enqueued: 5
Thread 8 dequeued: 1
Thread 9 dequeued: 2
β‘οΈ Queue operations are thread-safe!
π Example 3: Using ConcurrentStack in Threads
ConcurrentStack<T>
is a LIFO (Last In, First Out) collection.
β Code Example
using System;
using System.Collections.Concurrent;
using System.Threading;
class Program
{
static ConcurrentStack<int> stack = new ConcurrentStack<int>();
static void PushData(int value)
{
stack.Push(value);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} pushed: {value}");
}
static void PopData()
{
if (stack.TryPop(out int result))
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} popped: {result}");
}
}
static void Main()
{
for (int i = 1; i <= 5; i++)
{
int value = i;
new Thread(() => PushData(value)).Start();
}
Thread.Sleep(1000);
new Thread(PopData).Start();
new Thread(PopData).Start();
}
}
β Expected Output
Thread 3 pushed: 1
Thread 4 pushed: 2
Thread 5 pushed: 3
Thread 6 pushed: 4
Thread 7 pushed: 5
Thread 8 popped: 5
Thread 9 popped: 4
β‘οΈ Stack operations are thread-safe!
π Real-World Example: Processing Requests in a Web Server
A web server processes requests in parallel.
- Requests are stored in a queue (FIFO).
- Multiple threads handle these requests.
- Thread-safe collections in C# ensure no request is lost!
π Key Takeaways
β‘οΈ Thread-safe collections in C# prevent race conditions and data corruption.
β‘οΈ Use ConcurrentDictionary
, ConcurrentQueue
, ConcurrentStack
, and ConcurrentBag
for safe multithreading.
β‘οΈ These collections work without locks, making them efficient!
Β
π Next What?
π You did it! Now you understand Thread-safe collections in C# and how they make multithreading safer!
But waitβ¦ π€
What if two threads wait for each other forever? π± Deadlocks!
π₯ Next up: Deadlocks & Common Pitfalls in C# β How to Avoid Them! Stay tuned! π