What the GIL is
The Global Interpreter Lock is a single mutex inside the CPython interpreter. Any thread that wants to execute Python bytecode must first hold it, so only one thread runs Python code at a time, even on a 32-core machine. It is a property of the CPython implementation, not the language: Jython and IronPython have no GIL.
import threading
counter = 0
def work():
global counter
for _ in range(1_000_000):
counter += 1 # load, add, store: three separate bytecodes
ts = [threading.Thread(target=work) for _ in range(4)]
Running those four threads does not use four cores for the loop; they take turns holding the GIL. For CPU-bound Python, threads give concurrency (interleaving) but not parallelism (running at the same instant).

