In this article, we’ll cover:
- What is the Global Interpreter Lock (GIL)
- Why Python has GIL
- Threading in Python
- Multithreading explained
- How GIL affects performance
- Why Python still supports multithreading
1️⃣ Global Interpreter Lock (GIL)
The Global Interpreter Lock (GIL) is a mutex that allows only one thread to execute Python bytecode at a time.
Even on multi-core systems, Python threads cannot execute Python bytecode truly in parallel within a single process.
Why Does Python Have GIL?
Python internally uses:
- Reference counting
- Automatic memory management
Without GIL:
- Multiple threads could modify memory simultaneously
- Race conditions could occur
- Memory corruption could happen
The GIL keeps CPython memory management thread-safe.
Basic Example of GIL
import threading
counter = 0
def increment():
global counter
for _ in range(1000000):
counter += 1
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(counter)
Although two threads are running, only one thread executes Python bytecode at a time.
2️⃣ Real-World GIL Scenarios
CPU-intensive tasks do NOT benefit much from threading because of GIL.
Examples:
- Image processing
- AI computations
- Mathematical calculations
- Data compression
CPU-Bound Example
def heavy_computation():
total = 0
for i in range(10**7):
total += i
return total
Threads here compete for GIL instead of executing truly in parallel.
I/O tasks benefit greatly from threading because threads release the GIL while waiting.
Examples:
- API calls
- Database queries
- File operations
- Web scraping
I/O-Bound Example
import threading
import time
def fetch_data():
print("Fetching data...")
time.sleep(2)
print("Data fetched")
t1 = threading.Thread(target=fetch_data)
t2 = threading.Thread(target=fetch_data)
t1.start()
t2.start()
t1.join()
t2.join()
3️⃣ Threading in Python
Threading allows multiple tasks to run concurrently inside the same process.
Python provides threading via:
import threading
Basic Thread Example
import threading
def task():
print("Thread is running")
thread = threading.Thread(target=task)
thread.start()
thread.join()
4️⃣ Multithreading in Python
Multithreading means running multiple threads concurrently.
Multithreading Example
import threading
import time
def worker(name):
for i in range(3):
print(f"{name} working...")
time.sleep(1)
t1 = threading.Thread(target=worker, args=("Thread-1",))
t2 = threading.Thread(target=worker, args=("Thread-2",))
t1.start()
t2.start()
t1.join()
t2.join()
✅ Benefits of Multithreading
- Better responsiveness
- Efficient waiting-time usage
- Improved throughput
- Useful for backend systems
❌ Limitations of Multithreading
- GIL blocks true parallelism for CPU-heavy tasks
- Context switching overhead
- Synchronization complexity
5️⃣ How GIL Affects Multithreading
Only one thread executes Python bytecode at a time.
Even if:
- 4 CPU cores exist
- 4 threads are created
Python threads still compete for GIL.
Visualization
Thread-1 → Running
Thread-2 → Waiting
Thread-3 → Waiting
Thread-4 → Waiting
CPU-Bound Example
def compute():
sum(i * i for i in range(10**7))
Threads provide little performance gain.
I/O-Bound Example
import requests
requests.get("https://example.com")
Threads help significantly because waiting releases the GIL.
6️⃣ Solution for CPU-Bound Problems
Use:
- multiprocessing
- NumPy
- Async systems
- C extensions
Multiprocessing Example
from multiprocessing import Process
def task():
print("Running process")
p1 = Process(target=task)
p2 = Process(target=task)
p1.start()
p2.start()
p1.join()
p2.join()
Each process gets:
- Separate Python interpreter
- Separate GIL
7️⃣ Why Python Still Supports Multithreading
A common interview question:
If GIL exists, why does Python allow multithreading?
Because most real-world applications are I/O-bound.
Examples:
- Web servers
- APIs
- File systems
- Database services
Threads improve responsiveness while waiting for external resources.
Top comments (0)