Python Global Interpreter Lock (GIL)
What is the GIL?
The Global Interpreter Lock (GIL) is a lock built into Python's interpreter that allows only one thread to use the interpreter at a time.
- When a thread starts executing, it acquires the lock
- No other thread can execute until the current thread releases the lock
- This means Python threads run concurrently (taking turns) — never truly in parallel
The GIL is a software limitation of Python's interpreter — not a hardware limitation. Your CPU may have many logical cores, but Python's GIL prevents threads from using them simultaneously.
Impact on Python Programs
| Threading model | Possible in Python? |
|---|---|
| Concurrent (threads take turns) | ✅ Yes |
| Parallel (threads run simultaneously) | ❌ No — blocked by the GIL |
If you create 4 Python threads, they run one at a time — not all 4 at once. This is why multi-processing (true parallelism) is not demonstrated in Python the same way it is in other languages.
Why Does the GIL Exist?
1. Shared Memory Between Threads
All threads in a Python process share the same memory space. This means: - A variable created in Thread 1 can be read and modified by Thread 2, 3, 4... - If multiple threads modify the same object at the same time, the results become unpredictable
Example of what can go wrong without the GIL: - Thread 1 thinks a list has 10 elements - Thread 2 deletes one element at the same time - Thread 1 now has inconsistent data — bugs and crashes follow
The GIL prevents this by ensuring only one thread runs at a time.
2. Performance — Avoiding Many Small Locks
The alternative to the GIL is placing individual locks on every shared object (this is what other languages do): - Every time a thread accesses a list, dict, or any object → acquire a lock - Every time it's done → release the lock - Acquiring and releasing locks costs time and reduces performance - Python tested this approach — it made the interpreter slower, not faster
The GIL is one single lock instead of thousands of small locks = simpler and faster for single-threaded use cases.
3. C Extensions
Python runs a lot of performance-heavy operations using C code extensions behind the scenes (e.g. sorting algorithms, mathematical operations). These C extensions were designed to work with the GIL. Removing the GIL would break many of them.
4. Reference Count / Garbage Collection
Python tracks whether objects in memory are still needed using a reference count: - Every variable pointing to an object increments the count - When a variable is deleted or reassigned, the count decreases - When the count reaches 0 → Python removes the object from memory
Without the GIL, multiple threads could modify the reference count of the same object simultaneously, leading to: - A reference count appearing to be 0 when it isn't → object deleted while still needed ❌ - A reference count appearing to be positive when it isn't → memory never freed (memory leak) ❌
The GIL ensures reference counting is always accurate.
GIL vs Per-Object Locks
| Approach | How it works | Drawback |
|---|---|---|
| GIL (Python's choice) | One lock on the entire interpreter | No true parallelism |
| Per-object locks | Individual locks on each shared resource | High overhead; slower; very complex |
Python chose simplicity and single-threaded performance over parallel capability.
Key Takeaways & Recap
| Concept | Summary |
|---|---|
| GIL | A single lock that allows only one thread to execute Python code at a time |
| Effect | Python threads are concurrent (not parallel) |
| Why it exists | Shared memory safety, reference counting, C extension compatibility, performance |
| Hardware vs software | Not a CPU limitation — a Python interpreter design decision |
| Other languages | Most don't have a GIL; they use per-object locks or other mechanisms |
| Mutex | The formal name for a lock like the GIL — covered more in later lessons |