Threads, Processes and the GIL

What the interpreter lock actually locks, why an increment can lose a count, and when a process is the only thing that helps - with the mechanism runnable on every page.

7 modules Free, no login Updated 21 October 2026

About this track

Python's threads are real operating-system threads that are not allowed to run Python at the same time. One lock - the GIL - is held by whichever thread is executing bytecode, which is why threads help a program that waits and do nothing for a program that computes.

This track builds that from the bytecode up: the three operations an increment compiles to, the switch that lands between them, the lock protocol that prevents it, and the pickle boundary that decides what a process can be sent. A note on every page: the browser interpreter has no OS threads, so the editors run the mechanism and the thread-spawning examples are shown with their output.

Where a page's point depends on real operating-system threads, which the browser interpreter does not have, the example is shown with its recorded output and the mechanism underneath it - the bytecode, the lock protocol, the pickling boundary - runs live.

What you will be able to do

How the track is ordered

The interpreter lock comes first, because it decides when threads help at all. Race conditions follow, shown at the bytecode level so the lost update is a mechanism rather than a mystery, and locks come next as the fix, along with the ways a fix goes wrong. Threads and processes are then compared directly, concurrent.futures shows one interface over both, and a thread-safe queue replaces most hand-rolled coordination. The last page puts it together as a decision. It assumes comfortable Python and nothing about operating systems.

Where this leads

The Async Python track is the third option the final page weighs, and the right one for programs that mostly wait on the network. For numerical work, NumPy releases the interpreter lock inside many of its own loops, which is part of why vectorised code and threads can combine better than plain Python and threads.

All 7 modules, in teaching order

  1. 01The GIL, and What It Actually LocksSee what Python's GIL actually protects, why threads still speed up I/O-bound programs, and why they do nothing for CPU-bound code.
  2. 02Race Conditions: Why x += 1 Is Three OperationsRun x += 1 from two threads, watch the count drift, and trace the three bytecodes that open the gap where a value goes missing.
  3. 03Locks, and the Four Ways They Go WrongUnderstand deadlocks, livelocks, race conditions and how to avoid them in concurrent code.
  4. 04Threads or Processes: What Is and Is Not SharedCompare threads and processes side by side: shared memory versus isolation, the GIL constraint, and the real cost of sending data between workers.
  5. 05concurrent.futures: One Interface for BothSubmit work to a ThreadPoolExecutor and ProcessPoolExecutor through the same API, then inspect a Future's state transitions step by step.
  6. 06Handing Work Between Threads with a QueuePass work between threads with a queue that already wraps a lock and a condition, and see why it replaces most hand-rolled coordination.
  7. 07Choosing Between Threads, Processes and AsyncChoose between threads, processes, or async based on your I/O pattern and GIL constraints.

Other tracks