Under The Hood
backend python CPython 3.12+ (3.13 free-threaded builds exist)

Python: the GIL and asyncio

Last updated
Prerequisites:
Python threading and async basics
Understanding of OS threads and event loops
  • python
  • gil
  • asyncio
  • concurrency
  • cpython
  • threading

Read at your depth

The practical view

The GIL (Global Interpreter Lock) is a mutex in CPython that allows only one thread to execute Python bytecode at a time. That is why threading in CPython does not speed up CPU-bound work: threads interleave but never run in parallel on multiple cores. For I/O-bound work (network, disk), threads still win because the GIL is released during blocking calls. asyncio is single-threaded cooperative concurrency: async def functions, await points, and an event loop (asyncio.run(...)) that switches between coroutines when they await I/O. Use asyncio for high-concurrency I/O; use multiprocessing (separate interpreters, no GIL sharing) for CPU-bound parallelism.

Legacy vs modern

Thread pool for CPU work vs asyncio for I/O work

Threads cannot parallelize Python bytecode under the GIL; asyncio scales concurrent I/O on a single thread without OS-thread overhead.

before → after
Thread pool for CPU-bound work
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(8) as pool:
    results = list(pool.map(cpu_heavy, data))
asyncio for concurrent I/O
async def main():
    async with aiohttp.ClientSession() as session:
        results = await asyncio.gather(*(fetch(session, u) for u in urls))

Interview gotchas

Under The Hood — a multi-depth technical interview hub.

Press ⌘ K to search.