Concurrency & Threading
Understand the GIL, threading, and multiprocessing for CPU-bound tasks.
The Global Interpreter Lock (GIL)
The GIL is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once. This means multithreading in Python is great for I/O but not for CPU-bound tasks.
Threading (for I/O)
import threading
def worker(num):
print(f"Thread {num} working")
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
def worker(num):
print(f"Thread {num} working")
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
Multiprocessing (for CPU)
To bypass the GIL and use multiple CPU cores, use the multiprocessing module, which creates separate memory spaces.
import multiprocessing
def compute(n):
return n * n
if __name__ == '__main__':
with multiprocessing.Pool(processes=4) as pool:
results = pool.map(compute, range(100))
def compute(n):
return n * n
if __name__ == '__main__':
with multiprocessing.Pool(processes=4) as pool:
results = pool.map(compute, range(100))
Choosing the Right Tool
| Approach | Best for | Overhead |
|---|---|---|
| asyncio | High-concurrency I/O | Very Low |
| threading | Standard I/O | Low |
| multiprocessing | CPU-bound tasks | High |
โ Practice (30 minutes)
- Write a CPU-heavy function (like calculating primes) and time its execution.
- Run it using
threadingand observe no performance gain. - Run it using
multiprocessing.Pooland see the speedup. - Use
concurrent.futures.ThreadPoolExecutorfor a simpler threading interface.