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
awaitcall is made on an I/O operation (e.g.,await ReadAsync()):- The thread starts the I/O request.
- Instead of blocking, the thread is released to do other work (like keeping the UI responsive).
- 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.