1from threading import Thread2count = 03def work():4 global count5 count += 16t = Thread(target=work)7t.start(); work()8t.join(); print(count)process 1process 2maintcountCPU0101mainGILcountread 0read 0lost updateGILstart()count + 1count + 1Process
Code card: count.py; the highlighted line is the one runningThreads: workers that follow the code; the GIL key shows which one may runProcesses: running programs, each with its own memory and its own GILCores: a lamp lights while a core runs Python codeA process is a running program with its own memory; its threads share that memory. In CPython only the thread holding the GIL runs Python code, so the threads of one process take turns on one core. Each step lights the parts it is about. The yellow GIL key hops between the threads, count changes as they write to the one shared box, and a badly timed switch loses an update. A second process has its own interpreter, memory and GIL: both cores get busy, but the child's count is its own.

Processes and threads

  1. Running python count.py starts a process: one program in action, with its own private memory. Line 2, count = 0, puts the variable count into that memory.

The GIL

More processes

Try it yourself

;; step 1/11 · line 2: count = 0
$ python count.py        # the OS starts one process

>>> import os
>>> os.getpid()          # every process has an id
48213                    # yours will differ

The GIL belongs to CPython, the standard Python from python.org, not to the Python language. Since Python 3.13 an optional free-threaded build of CPython can run without the GIL (PEP 703); the usual build still has it. multiprocessing starts children with spawn on Windows and macOS and with forkserver on Linux since Python 3.14. Both import your file again in the child, which is why the start code sits under if __name__ == "__main__":.