Skip to content
  • Concurrency: Threads, Processes & asyncio · Python

Concurrency: Threads, Processes & asyncio

LearnViaNotes @learnvianotes· 4 min read

Three tools for doing many things at once. Which one to pick depends on whether your program waits on I/O or burns CPU — because of the GIL.

threadingmultiprocessingasyncio
ModelOS threads, shared memorySeparate processes, separate memoryOne thread, cooperative tasks
Best forI/O-bound (network, disk)CPU-bound (math, image processing)Many concurrent I/O waits (thousands of sockets)
GILLimits CPU parallelismBypassed — each process has its ownNot relevant — single thread
CostLight; needs locks for shared dataHeavy start-up; data must be pickledVery light; code must be async

Concurrency vs parallelism

The GIL in detail

  • What it protects: CPython's internals — above all the reference counts on every object. Without it, two threads doing refcount += 1 could corrupt memory.

  • When it is released: during blocking I/O (socket.recv, file.read, time.sleep), and inside many C extensions doing heavy work (NumPy, hashlib, zlib). The running thread is also forced to hand it over every few milliseconds (sys.getswitchinterval(), 5 ms by default).

  • What it does NOT do: make your code thread-safe. counter += 1 is several bytecodes, and a switch can happen between them — you still need locks.

  • Free-threaded build: CPython 3.13 added an optional build without the GIL (python3.13t); in 3.14 it is officially supported (PEP 779) as python3.14t. Pure-Python threads then run truly in parallel, at a small single-thread cost.

Python
import sys
print(sys.getswitchinterval())                 # 0.005
# Only exists on 3.13+; True on the normal build, False on a free-threaded one:
gil = getattr(sys, "_is_gil_enabled", lambda: True)()
print(isinstance(gil, bool))                   # True

Which tool? A decision flow

  1. Is the work mostly waiting?

    Network calls, disk, databases, sleeping → it's I/O-bound.

  2. I/O-bound, a handful to hundreds of tasks

    ThreadPoolExecutor — works with ordinary blocking libraries like requests.

  3. I/O-bound, thousands of connections

    asyncio with async libraries (aiohttp, httpx, asyncpg) — one thread, tiny per-task cost.

  4. Is it computing in Python?

    Loops, parsing, math → CPU-bound: ProcessPoolExecutor / multiprocessing, or vectorise with NumPy, or a free-threaded build.

  5. Mixed?

    asyncio for the I/O, loop.run_in_executor / asyncio.to_thread or a process pool for the heavy bits.

Threads for I/O

Python
from concurrent.futures import ThreadPoolExecutor
import time

def fetch(i):
    time.sleep(0.2)              # stands in for a network call (releases the GIL)
    return i * i

start = time.perf_counter()
with ThreadPoolExecutor(max_workers=5) as pool:
    results = list(pool.map(fetch, range(5)))
elapsed = time.perf_counter() - start
print(results, elapsed < 0.5)    # [0, 1, 4, 9, 16] True

Processes for CPU

Python
from concurrent.futures import ProcessPoolExecutor

def heavy(n):
    return sum(i * i for i in range(n))

if __name__ == "__main__":       # required: child processes re-import this module
    with ProcessPoolExecutor() as pool:
        print(list(pool.map(heavy, [10**6] * 4)))

asyncio for massive I/O concurrency

Python
import asyncio

async def download(name, delay):
    await asyncio.sleep(delay)   # yields control while "waiting"
    return f"{name} done"

async def main():
    results = await asyncio.gather(download("a", 0.1), download("b", 0.1))
    print(results)               # ['a done', 'b done']

asyncio.run(main())

Shared state needs a lock

Python
import threading

counter = 0
lock = threading.Lock()

def work():
    global counter
    for _ in range(100_000):
        with lock:               # without the lock, updates can be lost
            counter += 1

threads = [threading.Thread(target=work) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()
print(counter)                   # 400000

If Python has a GIL, why are threads useful at all?

Does the GIL make my code thread-safe?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report