Process vs Thread0%

Process vs Thread

Beginner12 min readUpdated: Jul 11, 2026
Study Materials

Process vs Thread: Deep Architectural Comparison

Choosing the appropriate concurrency model is one of the most critical engineering decisions in systems programming. In Python, the choice between Threads (threading) and Processes (multiprocessing) impacts memory footprint, throughput, fault tolerance, and developer ergonomics.


1. Operating System Mechanics: Process vs Thread

At the OS kernel level, processes and threads represent different levels of isolation:

Output
┌────────────────────────────────────────────────────────────────────────┐
│ OS Virtual Memory Space │
│ │
│ Process 1 (PID 100) Process 2 (PID 200) │
│ ┌──────────────────────────────┐ ┌───────────────────────────┐ │
│ │ Private Virtual Memory Space │ │ Isolated Address Space │ │
│ │ (Page Tables, MMU, File FDs) │ │ (Cannot read Process 1) │ │
│ │ │ │ │ │
│ │ Thread 1 Thread 2 │ │ Thread 3 │ │
│ │ ┌─────────┐ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ Stack │ │ Stack │ │ │ │ Stack │ │ │
│ │ └─────────┘ └─────────┘ │ │ └─────────┘ │ │
│ │ Shared Heap & Global State │ │ Private Heap │ │
│ └──────────────────────────────┘ └───────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘

Key Differences

MetricThread (threading)Process (multiprocessing)
Memory IsolationShared heap, global variables, and open file descriptorsCompletely isolated address space; memory is private
Context SwitchingLow overhead: CPU only swaps registers and stack pointersHigh overhead: OS must flush CPU TLB caches and swap page tables
Data SharingDirect pointers in shared RAM (requires mutex locks)Inter-Process Communication (IPC): requires pickling/serialization
Crash IsolationZero: A segfault in any thread kills the entire processHigh: A crash in a worker process leaves parent and siblings running
CPython GIL ImpactBound by the single GIL; cannot utilize multiple CPU coresEach process has its own GIL; full multi-core CPU parallelism
Startup CostLow (~microsecond scale)High (~millisecond scale; requires spawning or forking an interpreter)

2. Quantitative Benchmark: CPU-Bound vs I/O-Bound

The table below summarizes the performance behavior of threads vs processes in CPython:

Python
import concurrent.futures
import math
import os
import time
 
# Workload A: CPU-bound computation
def cpu_heavy_task(n: int) -> int:
return sum(math.isqrt(i) for i in range(n))
 
# Workload B: I/O-bound simulated network latency
def io_heavy_task(duration: float) -> None:
time.sleep(duration)
 
if __name__ == "__main__":
cpu_n = 4_000_000
tasks = 4
 
print("--- 1. CPU-BOUND BENCHMARK (4 Heavy Tasks) ---")
 
# Multithreading (Contends for single GIL)
start = time.perf_counter()
with concurrent.futures.ThreadPoolExecutor(max_workers=tasks) as executor:
list(executor.map(cpu_heavy_task, [cpu_n] * tasks))
thread_cpu_time = time.perf_counter() - start
print(f"ThreadPoolExecutor (CPU): {thread_cpu_time:.2f}s")
 
# Multiprocessing (Parallel execution on 4 cores)
start = time.perf_counter()
with concurrent.futures.ProcessPoolExecutor(max_workers=tasks) as executor:
list(executor.map(cpu_heavy_task, [cpu_n] * tasks))
proc_cpu_time = time.perf_counter() - start
print(f"ProcessPoolExecutor (CPU): {proc_cpu_time:.2f}s (Speedup: {thread_cpu_time/proc_cpu_time:.1f}x)")
 
print("\n--- 2. I/O-BOUND BENCHMARK (4 Simulated Latency Tasks) ---")
 
# Multithreading (Releases GIL during sleep/network)
start = time.perf_counter()
with concurrent.futures.ThreadPoolExecutor(max_workers=tasks) as executor:
list(executor.map(io_heavy_task, [0.2] * tasks))
thread_io_time = time.perf_counter() - start
print(f"ThreadPoolExecutor (I/O): {thread_io_time:.2f}s")
 
# Multiprocessing (Also parallel, but with IPC & spawn overhead)
start = time.perf_counter()
with concurrent.futures.ProcessPoolExecutor(max_workers=tasks) as executor:
list(executor.map(io_heavy_task, [0.2] * tasks))
proc_io_time = time.perf_counter() - start
print(f"ProcessPoolExecutor (I/O): {proc_io_time:.2f}s")

3. Fault Tolerance & Failure Isolation

In high-reliability backend systems, fault isolation is a critical consideration. If third-party C-extension code (e.g. OpenCV, TensorFlow, or a legacy C shared library) crashes via a null-pointer dereference or segmentation fault:

  • Threaded Model: The entire application (all threads, all user sessions) crashes immediately.
  • Multiprocess Model: Only the child worker process dies. The parent supervisor detects the non-zero exit code (exitcode < 0 indicates killed by signal) and can respawn a replacement worker without dropping service availability.
Python
import multiprocessing
import os
import signal
import time
 
def fragile_worker():
print(f"[WORKER {os.getpid()}] Processing payload...")
time.sleep(0.2)
# Simulate a fatal segmentation fault via SIGSEGV
os.kill(os.getpid(), signal.SIGTERM)
 
if __name__ == "__main__":
p = multiprocessing.Process(target=fragile_worker)
p.start()
p.join()
 
print(f"[SUPERVISOR] Worker process exited with code: {p.exitcode}")
if p.exitcode != 0:
print("[SUPERVISOR] Detected worker failure! Spawning healthy replacement worker...")

4. Concurrency Decision Matrix

Use this systematic checklist when architecting Python systems:

Output
What is your bottleneck?
┌───────────────────────┴───────────────────────┐
▼ ▼
CPU-BOUND I/O-BOUND
(Math, Hashing, Compression, (Network, REST APIs, Disk,
Image transformations) Database)
│ │
▼ How many concurrent connections?
MULTIPROCESSING │
(ProcessPoolExecutor) ┌───────────────┴───────────────┐
▼ ▼
< 1,000 Tasks > 1,000 Tasks
(Moderate I/O) (High-scale I/O)
│ │
▼ ▼
THREADING ASYNCIO
(ThreadPoolExecutor) (Event Loop / Tasks)

5. Architectural Summary Table

CriterionThreadingMultiprocessingAsyncio
Best ForModerate I/O-bound tasksHeavy CPU-bound computationMassive concurrent I/O (sockets, web)
Concurrency TypePreemptive multitaskingPreemptive parallel executionCooperative single-threaded concurrency
Hardware Utilization1 CPU core at a timeAll available CPU cores1 CPU core
Data SharingShared heap (use Locks)IPC / Pipes / SharedMemoryShared heap (single thread, no data race)
Fault IsolationPoor (shared crash)High (isolated processes)Medium (uncaught exception bubbles)

Multiple Choice Questions

1.

What occurs at the operating system level during a process context switch that makes it significantly more expensive than a thread context switch? A. The computer must be rebooted. B. The OS must switch page table mappings in the Memory Management Unit (MMU) and invalidate the CPU Translation Lookaside Buffer (TLB). C. All disk drives are unmounted. D. The Global Interpreter Lock is uninstalled.

Answer: B
Explanation:Process context switching requires changing the active virtual memory address space, forcing the MMU page table base register to update and clearing cached memory translations in the CPU TLB cache.

2.

Why is multiprocessing preferred over threading for CPU-intensive mathematical simulations in Python? A. Multiprocessing does not require importing standard libraries. B. Each process runs its own CPython interpreter instance with a private GIL, allowing simultaneous execution across multiple CPU cores. C. Threads cannot execute loops. D. Multiprocessing bypasses operating system security.

Answer: B
Explanation:In CPython, the GIL limits bytecode execution to one thread at a time per interpreter. By spawning separate processes, each process has its own interpreter and GIL, enabling true parallel execution across cores.

3.

In an application utilizing multithreading, what happens if one worker thread encounters a segmentation fault inside a compiled C-extension? A. Only that specific thread exits; other threads continue running. B. The entire operating system process crashes immediately, terminating all threads. C. CPython catches the segmentation fault and turns it into a KeyError. D. The thread restarts automatically.

Answer: B
Explanation:Threads share the process's single address space. An unrecoverable low-level memory fault (like a segfault) corrupts the process image and causes the operating system kernel to terminate the entire process.

4.

When would threading (or asyncio) be preferred over multiprocessing for an I/O-bound web crawler? A. When you want to maximize CPU cache thrashing. B. Because threads have lower memory overhead and avoid the serialization (pickling) costs associated with inter-process communication. C. Because processes cannot establish internet connections. D. Because threads run faster on GPUs.

Answer: B
Explanation:Spawning hundreds of processes consumes gigabytes of memory and incurs heavy IPC serialization overhead. Threads and asynchronous coroutines are much lighter and ideal for waiting on I/O.

5.

How is data transferred between separate processes in Python's multiprocessing module by default? A. Direct raw pointer dereferencing in memory. B. Data is serialized into byte streams using pickle and transferred via inter-process communication (IPC) pipes or queues. C. Data is written to optical discs. D. Through global environment variables.

Answer: B
Explanation:Because processes have isolated virtual memory spaces, Python serializes data objects using the pickle protocol and sends the bytes through operating system pipes or sockets.

Next Lesson

Shared Memory & Queues

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext