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! πŸ’‘

πŸ€” 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#

  1. ConcurrentDictionary<TKey, TValue> – Thread-safe key-value storage.
  2. ConcurrentQueue<T> – A thread-safe queue (FIFO).
  3. ConcurrentStack<T> – A thread-safe stack (LIFO).
  4. 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! πŸš€

Leave a Comment

Share this Doc

Thread-safe collections

Or copy link