What is the GIL (Global Interpreter Lock) and how does it impact Python concurrency?
The Global Interpreter Lock (GIL) is a mutex in CPython that allows only one thread to execute Python bytecode at a time, even on multi-core machines. It exists to protect CPython's memory management (reference counting) from race conditions.
Impact:
- CPU-bound tasks: Threads do NOT run in parallel. Multiple threads actually slow things down due to context-switching overhead.
- I/O-bound tasks: Threads work well because the GIL is released during I/O operations (network calls, file reads, sleep).
- C extensions: Libraries like NumPy release the GIL during heavy computation, enabling true parallelism.
Workarounds for CPU-bound parallelism:
multiprocessing-- separate processes, each with its own GILconcurrent.futures.ProcessPoolExecutor- C extensions that release the GIL (NumPy, Cython)
subinterpreters(Python 3.12+)- Free-threaded Python (Python 3.13+ experimental, no GIL)
Note: The GIL is a CPython implementation detail. Jython and IronPython do not have a GIL.
Thread 1: acquire GIL -> execute bytecode -> release GIL
Thread 2: waiting... -> acquire GIL -> execute bytecode -> release GIL
I/O-bound: Thread releases GIL during I/O wait
CPU-bound: Threads take turns but never truly parallel
import threading
import time
# Demonstrating GIL impact on CPU-bound work
def cpu_bound(n):
count = 0
for i in range(n):
count += i
return count
# Sequential
start = time.perf_counter()
cpu_bound(10_000_000)
cpu_bound(10_000_000)
seq_time = time.perf_counter() - start
print(f"Sequential: {seq_time:.3f}s")
# Threaded (NOT faster due to GIL)
start = time.perf_counter()
t1 = threading.Thread(target=cpu_bound, args=(10_000_000,))
t2 = threading.Thread(target=cpu_bound, args=(10_000_000,))
t1.start(); t2.start()
t1.join(); t2.join()
thread_time = time.perf_counter() - start
print(f"Threaded: {thread_time:.3f}s") # Similar or slower than sequential!
# I/O-bound: threads DO help
import urllib.request
def fetch_url(url):
urllib.request.urlopen(url)
# GIL is released during network I/O, so threads overlap
- •GIL allows only one thread to execute Python bytecode at a time
- •Threading is effective for I/O-bound tasks (GIL is released during I/O)
- •Threading is NOT effective for CPU-bound tasks -- use multiprocessing instead
- •The GIL is a CPython-specific implementation detail
- •Even though threading doesn't provide CPU parallelism, it can make CPU-bound code SLOWER due to lock contention
- •GIL is released during I/O, C extension calls, and time.sleep -- so threads still have uses
- •Python 3.13 introduces experimental free-threaded mode (no GIL) via --disable-gil build
- •Some libraries (NumPy, PIL) release the GIL in C code, enabling true thread-level parallelism