Multithreading

Back

Loading concept...

🎭 The Restaurant Kitchen: A Story About Multithreading

Imagine you own a busy restaurant. You have ONE chef (your computer’s single thread). When 10 orders come in at once, that poor chef has to cook each dish one by one. Customers wait forever!

What if you hired MORE chefs? Now multiple dishes cook at the same time. That’s multithreading β€” having multiple workers (threads) doing tasks simultaneously.


🧡 Thread Basics: Your First Kitchen Helper

A Thread is like hiring a new chef. Each chef can work independently.

Creating Your First Thread

// Hire a new chef (create a thread)
Thread chef = new Thread(() => {
    Console.WriteLine("Chef is cooking!");
});

// Tell the chef to start working
chef.Start();

// Wait for chef to finish
chef.Join();

What happens here?

  1. new Thread(...) β€” We hire a chef
  2. Start() β€” Chef begins cooking
  3. Join() β€” We wait until chef finishes

Real Example: Two Chefs Working

Thread chef1 = new Thread(() => {
    for (int i = 0; i < 3; i++) {
        Console.WriteLine("Chef 1 cooking...");
        Thread.Sleep(100); // Takes time
    }
});

Thread chef2 = new Thread(() => {
    for (int i = 0; i < 3; i++) {
        Console.WriteLine("Chef 2 cooking...");
        Thread.Sleep(100);
    }
});

chef1.Start();
chef2.Start();

Output might be:

Chef 1 cooking...
Chef 2 cooking...
Chef 1 cooking...
Chef 2 cooking...

Notice how they work at the same time? That’s the magic!


🏊 ThreadPool: The Smart Staffing Agency

Hiring and firing chefs takes time. What if you had a staffing agency that keeps chefs ready?

That’s the ThreadPool β€” a pool of pre-made threads ready to work!

// Ask the agency for a chef
ThreadPool.QueueUserWorkItem(_ => {
    Console.WriteLine("Agency chef cooking!");
});

// Ask for another
ThreadPool.QueueUserWorkItem(_ => {
    Console.WriteLine("Another agency chef!");
});

Why Use ThreadPool?

Manual Threads ThreadPool
You hire each chef Agency provides chefs
Slow to create Already ready
You manage them Agency manages them
Good for long tasks Good for quick tasks

Simple Rule: Use ThreadPool for small, quick jobs. Use regular Threads for long, important tasks.


πŸ” Synchronization Primitives: The Kitchen Rules

Problem: Two chefs reach for the same pan at once. Disaster!

We need rules (synchronization primitives) to prevent chaos.

graph TD A["Two Threads"] --> B{Same Resource?} B -->|Yes| C["Need Synchronization!"] B -->|No| D["Safe - No Problem"] C --> E["Use Lock/Monitor/Mutex"]

πŸ”’ Lock vs Monitor vs Mutex: Three Types of Kitchen Locks

1. Lock β€” The Simple Door Lock

Like a bathroom door lock. One person at a time.

private object _lock = new object();
private int counter = 0;

void SafeIncrement() {
    lock (_lock) {
        // Only ONE thread here at a time
        counter++;
    }
}

2. Monitor β€” Lock with More Control

Same lock, but you can wait and signal others.

private object _lock = new object();

void WaitForFood() {
    lock (_lock) {
        // Wait until food is ready
        Monitor.Wait(_lock);
        Console.WriteLine("Food received!");
    }
}

void FoodIsReady() {
    lock (_lock) {
        // Tell waiting person
        Monitor.Pulse(_lock);
    }
}

3. Mutex β€” The Master Key

Works across different programs. Like a key that works on multiple buildings.

using Mutex mutex = new Mutex(false, "MyAppMutex");

mutex.WaitOne(); // Get the key
try {
    // Only ONE program can be here
    Console.WriteLine("I have the key!");
}
finally {
    mutex.ReleaseMutex(); // Return the key
}

Quick Comparison

Feature lock Monitor Mutex
Simple to use βœ… Yes Medium Complex
Cross-process ❌ No ❌ No βœ… Yes
Wait/Signal ❌ No βœ… Yes ❌ No
Speed Fast Fast Slower

Rule of Thumb:

  • 99% of time: Use lock
  • Need wait/signal: Use Monitor
  • Multiple programs: Use Mutex

⚠️ Race Conditions: When Chefs Collide

A race condition is when the result depends on who finishes first.

The Problem

int balance = 100;

// Thread 1: Withdraw 50
void Withdraw() {
    if (balance >= 50) {
        Thread.Sleep(1); // Tiny delay
        balance -= 50;
    }
}

// Thread 2: Also withdraw 50
// BOTH might withdraw!

What could happen:

  1. Thread 1 checks: balance is 100 βœ…
  2. Thread 2 checks: balance is 100 βœ…
  3. Thread 1 withdraws: balance = 50
  4. Thread 2 withdraws: balance = 0

Both withdrew, but we only had 100! πŸ’Έ

The Fix

private object _lock = new object();

void SafeWithdraw() {
    lock (_lock) {
        if (balance >= 50) {
            balance -= 50;
        }
    }
}

Now only ONE thread can check AND withdraw at a time.


πŸ“¦ Concurrent Collections: Thread-Safe Containers

Normal collections are NOT safe with multiple threads.

// DANGEROUS - Don't do this!
List<int> list = new List<int>();
// Multiple threads adding = CRASH!

Safe Alternatives

ConcurrentDictionary β€” Thread-safe dictionary

var dict = new ConcurrentDictionary<string, int>();
dict.TryAdd("apples", 5);
dict.AddOrUpdate("apples", 1, (k, v) => v + 1);

ConcurrentQueue β€” Thread-safe queue

var queue = new ConcurrentQueue<string>();
queue.Enqueue("Order 1");
queue.TryDequeue(out string order);

ConcurrentBag β€” Thread-safe unordered collection

var bag = new ConcurrentBag<int>();
bag.Add(1);
bag.Add(2);
bag.TryTake(out int item);

When to Use What

Collection Use When
ConcurrentDictionary Key-value lookups
ConcurrentQueue First-in-first-out
ConcurrentStack Last-in-first-out
ConcurrentBag Order doesn’t matter
BlockingCollection Producer-consumer pattern

πŸš€ Parallel.For and Parallel.ForEach: Turbo Mode!

Want to process a list with ALL your chefs at once?

Regular For Loop (One Chef)

// One chef, one dish at a time
for (int i = 0; i < 100; i++) {
    ProcessItem(i);
}

Parallel.For (All Chefs!)

// ALL chefs cooking at once!
Parallel.For(0, 100, i => {
    ProcessItem(i);
});

Parallel.ForEach Example

string[] dishes = { "Pizza", "Pasta", "Salad" };

// Process all dishes simultaneously
Parallel.ForEach(dishes, dish => {
    Console.WriteLine(quot;Cooking {dish}");
    Thread.Sleep(1000); // Each takes 1 second
});

// Total time: ~1 second (not 3!)

When to Use Parallel

graph TD A["Do I have many items?"] -->|Yes| B["Is each item independent?"] A -->|No| C["Use regular loop"] B -->|Yes| D["Is the work CPU-heavy?"] B -->|No| C D -->|Yes| E["Use Parallel!"] D -->|No| F["Maybe - test it"]

Important Rules

  1. Items must be independent β€” No chef should need another chef’s dish
  2. CPU-bound work β€” Good for calculations, NOT for waiting (use async for that)
  3. Test performance β€” Sometimes regular loops are faster for small data!

🎯 Summary: Your Multithreading Toolkit

Tool Purpose Example Use
Thread Manual worker Long background tasks
ThreadPool Quick workers Short, quick jobs
lock Simple protection Protect shared data
Monitor Advanced protection Wait/signal patterns
Mutex Cross-program lock Single app instance
Concurrent Collections Safe containers Shared lists/dictionaries
Parallel.For/ForEach Parallel processing Process large datasets

Golden Rules

  1. Start simple β€” Use lock before trying fancier stuff
  2. Avoid sharing β€” If threads don’t share data, no problems!
  3. Test thoroughly β€” Race conditions are sneaky
  4. Use Concurrent Collections β€” Don’t make regular collections β€œsafe”
  5. Parallel isn’t always faster β€” Measure before assuming!

🌟 You Did It!

You now understand:

  • βœ… How threads work (the chefs)
  • βœ… ThreadPool (the staffing agency)
  • βœ… Synchronization (the kitchen rules)
  • βœ… Lock vs Monitor vs Mutex (types of locks)
  • βœ… Race conditions (when chefs collide)
  • βœ… Concurrent collections (safe containers)
  • βœ… Parallel loops (turbo mode!)

Remember: Multithreading is like managing a restaurant kitchen. With the right tools and rules, your kitchen runs smoothly. Without them? Chaos! 🍳

Now go build something awesome! πŸš€

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.