What is the Python Global Interpreter Lock (GIL)

Last Updated : 9 Jun, 2026

Global Interpreter Lock (GIL) is a mutex (mutual-exclusion lock) used in the CPython interpreter (the default and most widely used Python implementation). It ensures that only one thread executes Python bytecode at a time, even if you have multiple CPU cores. This means:

  • Python supports multithreading,
  • BUT threads cannot run CPU-bound code in parallel inside CPython.

However, I/O-bound multithreading does work well because threads are allowed to release the GIL while waiting for I/O operations.

Need for the GIL in CPython

In CPython, every Python object uses reference counting for memory management.

Python
import sys

a = "Geek"
print(sys.getrefcount(a))

b = a
print(sys.getrefcount(a))

Output
4
5

Note: The exact reference count may vary across Python versions and environments.

The issue: If two threads simultaneously modify the reference count, it can cause: corrupted memory, inconsistent object states and crashes 

Possible solution: CPython developers could lock every object or memory update, but that would make Python much slower.

Final decision: Introduce one global lock so CPython designers added a single interpreter-wide lock, the GIL, which guarantees:

  • Thread-safe memory management
  • Simplicity of implementation
  • Fast performance for single-threaded programs

Factors That Led to the GIL

  1. CPython uses C at the core, and most extensions (NumPy, PIL, etc.) are written in C. The GIL made memory management extremely simple.
  2. Performance boost for single-threaded programs, A single lock is faster than thousands of fine-grained locks.
  3. Ease of implementation, CPython was designed in the early 1990s when multi-core processors were rare. The GIL was an easy and safe solution.

Impact of the GIL on Multi-threaded Programs

Depending on your workload, the GIL has different effects.

1. CPU-bound programs: Programs that do heavy calculations: loops, math, compression, hashing, etc. In a CPU-bound task, multiple threads do NOT run in parallel because the GIL does not allow it.

Note: The execution time varies depending on the hardware, operating system, Python version, and current system workload.

Python
import time

COUNT = 50_000_000

def countdown(n):
    while n > 0:
        n -= 1

start = time.time()
countdown(COUNT)
print("Time taken:", time.time() - start)

Output

Time taken: <varies by system>

Explanation: This example performs a CPU-intensive countdown operation using a single thread. It serves as a baseline for comparing the effect of multithreading on CPU-bound workloads.

2. CPU-bound with two threads (GIL blocks true parallelism): Despite two threads, the performance is almost the same because only one thread runs Python bytecode at ANY moment.

Python
import time
from threading import Thread

COUNT = 50_000_000

def countdown(n):
    while n > 0:
        n -= 1

t1 = Thread(target=countdown, args=(COUNT//2,))
t2 = Thread(target=countdown, args=(COUNT//2,))

start = time.time()
t1.start(); t2.start()
t1.join(); t2.join()
print("Time taken:", time.time() - start)

Output

Time taken: <varies by system>

Explanation: Although two threads are used, the GIL prevents them from executing Python bytecode simultaneously. As a result, CPU-bound tasks typically see little or no performance improvement from multithreading.

I/O-Bound Programs

Programs that wait for I/O: downloading files, reading files, database queries, API calls. Here, GIL does not slow you down because:

  • When a thread waits for I/O -> it releases the GIL.
  • Other threads can run during that time.

So I/O-bound multithreading works very well in Python.

Limitations of Removing the GIL

Removing the GIL is extremely difficult because:

  1. Too many libraries depend on it: NumPy, Pandas, OpenCV, and thousands of C-extension packages rely on the GIL.
  2. Without the GIL, Python would slow down: Many small locks would make Python slower for normal programs.
  3. Compatibility issues: Removing GIL breaks how existing Python code works.
  4. Previous attempts failed: They made Python slower and used more memory.

However, Python 3.13 introduces optional builds without the GIL, but it is still experimental.

Techniques for Overcoming GIL Limitations

1. Use Multiprocessing (Best for CPU-bound tasks): Each process has its own Python interpreter and its own GIL, so they run truly in parallel.

Python
import multiprocessing
import time

COUNT = 50_000_000

def countdown(n):
    while n > 0:
        n -= 1

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=countdown, args=(COUNT//2,))
    p2 = multiprocessing.Process(target=countdown, args=(COUNT//2,))

    start = time.time()
    p1.start(); p2.start()
    p1.join(); p2.join()
    print("Time taken:", time.time() - start)

Output

Time taken: <varies by system>

This uses multiple CPU cores.

2. Use libraries that release the GIL

  • NumPy
  • SciPy
  • OpenCV
  • Pandas (some operations)

These libraries run heavy computation in C and release the GIL, allowing parallel execution.

3. Use asyncio for I/O tasks: asyncio does not use multiple threads. It uses a single thread that switches tasks efficiently. Great for:

  • API calls
  • network tasks
  • database operations
Comment