Concurrency & Parallelism

Chapter 1: The Core Concepts - Threads vs Processes

Before we talk about doing two things at once, we need to understand WHO is doing the work.

1.1 Process - The Factory

A Process is an independent program running on your computer (like Chrome, Word, or your Server). Each process has its own memory space. They are isolated. If one crashes, the others are fine.

Analogy: A separate house. People in House A cannot see or touch anything in House B.

1.2 Thread - The Worker

A Thread sits INSIDE a process. A process can have multiple threads sharing the same memory.

Analogy: People living in the SAME house. They share the kitchen, bathroom, and TV. This is fast (easy to share) but dangerous (two people fighting over the TV remote).

Chapter 2: Concurrency vs Parallelism

These two words are often confused, but they mean very different things.

👨‍🍳 Real World: The Professional Kitchen

Concurrency (One Chef): You cut carrots, then put a pot on the stove, then chop onions while the water heats. You are "dealing with" multiple tasks at once, but you are only doing ONE physical thing at a time. This is how single-core CPUs work.

Parallelism (Two Chefs): You cut carrots WHILE your friend chops onions. Two things are actually happening at the exact same instant. This requires multi-core CPUs.

Key takeaway: Concurrency is about structure (handling multiple things). Parallelism is about execution (doing multiple things).

Chapter 3: The Danger Zone - Race Conditions

A Race Condition occurs when two threads try to change shared data at the same time, and the result depends on who "wins" the race. It usually results in corrupted data.

💵 Real World: The Joint Bank Account

Scenario: Balance is $100. Husband and Wife both want to withdraw $80.

  1. Husband checks balance: "$100" (Success! I can withdraw)
  2. Wife checks balance: "$100" (Success! I can withdraw)
  3. Husband starts transaction...
  4. Wife starts transaction...
  5. Husband takes $80. Balance becomes $20.
  6. Wife takes $80. Balance becomes -$60.

Result: Only one should have succeeded. The bank lost money because the checks happened before the updates completed.

In Code (C# Example)


// ❌ UNSAFE: Not Thread-Safe
public class BankAccount
{
    private int _balance = 100;

    public void Withdraw(int amount)
    {
        if (_balance >= amount)
        {
            // If two threads enter here at once, both will pass the check!
            Thread.Sleep(10); // Simulate processing time
            _balance -= amount;
        }
    }
}

Chapter 4: Safety Tools - Locks and Semaphores

How do we stop the race? We force threads to wait their turn.

4.1 The Lock (Mutex) - "The Bathroom Key"

A Lock ensures only ONE thread can enter a specific section of code at a time.

Analogy: A coffee shop bathroom. If the door is locked, you must wait outside until the person inside comes out.


// ✅ SAFE: Using a Lock
private readonly object _lock = new object();

public void Withdraw(int amount)
{
    lock (_lock) // Only 1 thread enters here
    {
        if (_balance >= amount)
        {
            _balance -= amount;
        }
    }
}

4.2 The Semaphore - "The Nightclub Bouncer"

A Semaphore allows a specific NUMBER of threads to enter.

Analogy: A nightclub capacity. The bouncer lets 50 people in. When one leaves, one enters. If capacity is 1, it behaves like a Lock.

Use Case: Limiting database connections (e.g., "Only run 10 queries at once").

Chapter 5: Deadlocks - The Standstill

Locks are safe, but can cause a new problem: Deadlock. This happens when two threads wait for each other forever.

🚦 Real World: Gridlock Intersection

Four cars meet at a 4-way stop.

  • Car A waits for Car B to go.
  • Car B waits for Car C to go.
  • Car C waits for Car D to go.
  • Car D waits for Car A to go.

Nobody moves. Ever.

Technical Example:
Thread 1 holds "Lock A" and wants "Lock B".
Thread 2 holds "Lock B" and wants "Lock A".
Both wait forever.

How to Avoid:
Always acquire locks in the same order. (e.g., Everyone must grab Lock A first, then Lock B).

Chapter 6: Async/Await - Doing More with Less

In web servers (like ASP.NET or Node.js), we use Async/Await instead of creating thousands of threads.

🍽️ Real World: The Waiter

Threaded Model (Blocking): A waiter takes an order, walks to the kitchen, and stands there staring at the chef for 15 minutes until the food is ready. He ignores all other tables. Terrible service!

Async Model (Non-Blocking): A waiter gives the order to the chef, then walks away to serve other tables. When the chef rings the bell ("callback/await"), the waiter comes back to pick up the food.

Key Concept: Async code frees up the thread while waiting for "slow" things (Database, API calls, File I/O). This allows one server to handle thousands of users.

The Golden Rule of Async

NEVER block the main thread.

If you put a Thread.Sleep(5000) or a heavy calculation loop in async code, you are effectively "freezing the waiter". The whole server stops responding.

Chapter 7: Summary Checklist

How to write thread-safe code:

  • [ ] Minimize Shared Mutable State. The best way to avoid race conditions is to not share data at all (use Immutable objects).
  • [ ] Use Locks for Critical Sections. If you must change shared data, lock it.
  • [ ] Avoid Deadlocks. Always acquire locks in a consistent order.
  • [ ] Use Async for I/O. Don't block threads waiting for databases.
  • [ ] Use Thread-Safe Collections. Don't use a standard List across threads used ConcurrentBag or ConcurrentDictionary.

Quick Review

Concurrency is structuring work so multiple tasks can make progress, while parallelism is executing tasks simultaneously on multiple CPU cores, allowing us to improve throughput and responsiveness when used appropriately.

✅ Core terms

  • Process: separate memory space (heavier, safer isolation).
  • Thread: shares memory inside a process (lighter, easier to race).

✅ The big risks

  • Race condition: outcome depends on timing (two threads update the same state).
  • Deadlock: threads wait forever (avoid by consistent lock ordering and short lock duration).

✅ The tools

  • Lock/Mutex: only one thread enters a critical section.
  • Semaphore: allow N concurrent entries (great for throttling DB/API calls).

✅ The modern server approach

  • Async/Await: best for I/O waits; it frees threads to serve other work.
  • Golden rule: don’t block the thread (no long sleeps or heavy CPU loops in the request path).