devnotes.

Multithreading and Multiprocessing

Python offers two different ways to run code in parallel, and understanding the GIL (Global Interpreter Lock) explains why each is suited to a different kind of workload.

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time

def io_task(n):            # simulates I/O — e.g. a network request
    time.sleep(1)
    return n * 2

def cpu_task(n):             # simulates heavy computation
    return sum(i * i for i in range(10_000_000))

# Threading — good for I/O-bound work
with ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(io_task, [1, 2, 3, 4]))   # runs concurrently, ~1s total

# Multiprocessing — good for CPU-bound work
with ProcessPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(cpu_task, [1, 2, 3, 4]))   # runs on separate CPU cores

CPython (the standard implementation) has a GIL — only one thread can execute Python bytecode at any given instant, even on a multi-core machine. This means threading in Python does not speed up CPU-bound work (heavy computation) at all, since the threads still take turns on a single core. But threading does help with I/O-bound work, because a thread waiting on a network response or disk read releases the GIL during that wait, letting another thread run — this is exactly why ThreadPoolExecutor genuinely speeds up the io_task example above.

Multiprocessing sidesteps the GIL entirely by running separate Python processes, each with its own interpreter and memory space, and genuinely runs on multiple CPU cores in parallel — the right tool for CPU-bound work like heavy numerical computation. The trade-off: processes are heavier to start than threads, and don't share memory directly, so passing data between them requires explicit serialization (handled automatically by ProcessPoolExecutor for simple cases).

As a rule of thumb: I/O-bound → asyncio or threading; CPU-bound → multiprocessing. Choosing the wrong tool for the workload is a common and easy-to-make Python performance mistake.

Multithreading and Multiprocessing in Python | DevNotes