Fundamental Abstractions: Process & Thread

  • Process virtualizes memory: The Operating System (OS) gives each running program (a process) its own private and isolated memory space. This protective bubble prevents processes from interfering with one another, which ensures stability.

  • Thread virtualizes the CPU: A thread is a path of execution inside a process. The OS creates the illusion that each thread has its own dedicated CPU core by rapidly switching between them (time-slicing). This fundamental concept enables concurrency and keeps applications responsive.

1. The Problem: Single-Threaded Applications

  • In a single-threaded application, if one task gets stuck (e.g., an infinite loop or a slow operation), the entire application freezes.
  • The Operating System (OS) prevents the whole system from freezing by using context switching. It rapidly switches between different processes, giving each a small time slice on a CPU core.

2. A Solution: Multithreading

  • Threads allow a single application to perform multiple tasks concurrently.

Myth: More Threads ≠ More Speed

More threads do not automatically make an application faster. They add overhead and can slow things down.

  • Thread Overheads:
    • Memory: Each thread requires memory for its context and environment block.
    • CPU Time: The OS spends CPU cycles creating, managing, and switching between threads.

3. Optimizing Thread Count

  • A single CPU core can only execute one thread at a time. True performance gains from multi-threading come from having multiple CPU cores.
  • For CPU-bound tasks (e.g., heavy calculations), the ideal number of threads is typically close to the number of CPU cores.
  • For O-bound tasks (e.g., network requests), this rule doesn’t apply, as threads spend most of their time waiting.

4. The I/O Problem: The Inefficiency of Blocking

  • When a thread performs an I/O (Input/Output) operation, it enters a blocked (waiting) state.
  • The CPU core is not idle—the OS immediately reassigns it to another ready thread.
  • The inefficiency is that a thread itself is wasted. An entire thread, with all its memory, is consumed just to wait, which is a poor use of system resources. If this is the UI thread, the application becomes unresponsive.

5. The Modern Solution: Asynchronous Programming (async/await)

  • Asynchronous programming solves the I/O problem efficiently, without needing to add more threads.
  • When an await call is made on an I/O operation (e.g., await ReadAsync()):
    1. The thread starts the I/O request.
    2. Instead of blocking, the thread is released to do other work (like keeping the UI responsive).
    3. When the I/O operation is finished, the method continues where it left off.

Key Benefit

This provides a readable, sequential programming model that is non-blocking, leading to far more scalable and responsive applications.