Stop by Stop

Concurrency line

The Python GIL

Python lets only one cook use the stove at a time, so hiring more cooks does not always make dinner faster.

Words to know

Stops explain each step. The film plays the whole ride.

Explain it
Route
One Python kitchenCook 1Has the hatCook 2Ready to cookThe chef’s hatCook 1 wears itThe stoveWhere cooking happensDelivery doorWhere parcels arriveRice cookerSome need no hat
  • Cooking
  • Waiting or asking
  • Handed over
  • Something went wrong
Stop 1 of 11

Cook 1 puts on the hat

Python’s kitchen has one stove with several burners, but only one chef’s hat. Only the cook wearing the hat may cook. Cook 1 puts it on.

Next stop: Cook 1 cooks on the stove

All stops

The everyday version

A kitchen has one stove and one chef’s hat. Only the cook wearing the hat may cook. Everyone else waits. A cook who steps away to wait at the delivery door hangs the hat back up.

  • The first cookis the Thread 1

    Puts on the hat, cooks, and hangs it back up when asked.

  • The second cookis the Thread 2

    Waits for the hat. After a few moments, politely asks for it.

  • The chef’s hatis the The GIL

    Only one exists. Whoever wears it is the only one allowed to cook.

  • The stoveis the Interpreter

    Where the cooking happens, with one cook at a time.

  • The delivery dooris the Network / disk

    Waiting for a parcel needs no stove, so the hat goes back on the hook.

  • The rice cookeris the C extension

    Cooks on its own while the cook does something else. A good one needs no hat at all.

  • Two long dishes, one hatis two cooks with endless work

    Both cooks need the stove all the time. More cooks will not speed it up, because only one may wear the hat.

Interview questions

Say the short answer first. Go deeper only if they ask.

What is the GIL and what does it protect?
Short answer

A mutex in CPython so only one thread executes Python bytecode at a time.

If they dig deeper

It keeps the object model, including built-ins like dict, safe against concurrent access, and simplifies reference counting. It trades multi-core parallelism for simplicity.

Answer that loses points

“It makes my code thread-safe.”

When is the GIL released?
Short answer

During blocking I/O, in C extensions that release it, and on forced switches.

If they dig deeper

The glossary says it is always released when doing I/O. Hashing, compression and numeric extensions often release it. A waiting thread also forces a switch after getswitchinterval(), default 5 ms.

Answer that loses points

“Never. Threads in Python are fake.”

Why do CPU-bound threads not speed up?
Short answer

Only one thread executes bytecode at a time.

If they dig deeper

Threads alternate on the GIL, so total CPU time is unchanged and switching adds overhead. Use processes, or native code that releases the GIL.

Answer that loses points

“Python threads are green threads on one core.”

What does sys.setswitchinterval do?
Short answer

It sets the ideal time slice, 5 ms by default, before a waiting thread requests the GIL.

If they dig deeper

It is advisory: the switch happens at the next eval-breaker check, and which thread runs next is the OS’s decision. Lower values add responsiveness and overhead.

Answer that loses points

“It is how many bytecodes run before a switch.”

Does the GIL make counter += 1 atomic?
Short answer

No.

If they dig deeper

It compiles to several bytecodes: load, add, store. A switch between them loses an update. The docs FAQ lists i = i+1 as not atomic. Use a Lock.

Answer that loses points

“Yes, the GIL makes everything atomic.”

Is the GIL removed in Python 3.14?
Short answer

No. Free-threaded builds are officially supported but optional. The GIL build is still the default.

If they dig deeper

3.13 introduced the experimental t build. PEP 779 made it supported in 3.14, with roughly 5-10% single-thread overhead. Extensions must support it, or the GIL is re-enabled.

Answer that loses points

“3.13 and 3.14 removed the GIL for everyone.”

What are the alternatives for CPU parallelism?
Short answer

Processes, native extensions, subinterpreters and free-threaded builds.

If they dig deeper

multiprocessing and ProcessPoolExecutor give each process its own GIL. NumPy, Cython or Rust can release it. 3.14 adds InterpreterPoolExecutor, built on the per-interpreter GIL of PEP 684.

Answer that loses points

“Rewrite it in Go” as the only answer, or “use asyncio.”

Does asyncio bypass the GIL?
Short answer

No. It is single-threaded and concurrent, not parallel.

If they dig deeper

Its win is cheap waiting on many sockets, not running Python on several cores. A CPU-heavy coroutine blocks the whole event loop.

Answer that loses points

“asyncio gets around the GIL.”

Cheat-sheet

Commands you will actually type.

import sys; sys.getswitchinterval()    # 0.005

Current switch interval in seconds.

sys.setswitchinterval(1e-6)

Teaching only: makes races easier to reproduce. Not a fix.

import sys; sys._is_gil_enabled()    # 3.13+

Is the GIL on right now?

python3.14t -X gil=0 script.py     # or PYTHON_GIL=0

Run a free-threaded build with the GIL off. `=1` forces it on.

import sysconfig; sysconfig.get_config_var("Py_GIL_DISABLED")  # 1 on a t build

Does this build support free threading?

import dis; dis.dis("counter += 1")

Shows several bytecodes that a switch can split.

import hashlib, threading   # hashing big buffers can overlap on threads

An example of C work that releases the GIL.

Sources