advanced
Multithreading in Python
7 min read
Overview
Multithreading allows a Python application to run multiple tasks concurrently within a single process. It is ideal for I/O-bound tasks (like downloading web pages, reading disk files, or querying network APIs).
Learning Objectives
- Use the
threadingmodule to launch threads. - Use
concurrent.futures.ThreadPoolExecutorfor worker pool management. - Understand how the Global Interpreter Lock (GIL) impacts thread concurrency.
Concept Explanation
In simple words:
- I/O-Bound Operations: When a thread waits for network data or disk I/O, Python releases the GIL so other threads can execute. Multithreading makes network downloads 5x–10x faster!
- CPU-Bound Operations: For heavy math calculation, the GIL prevents multiple threads from running CPU instructions simultaneously on multiple CPU cores. For CPU tasks, use Multiprocessing instead.
Code Examples
Example 1 — Concurrent Downloads with ThreadPoolExecutor
python
from concurrent.futures import ThreadPoolExecutor
import time
urls = [
"https://api.example.com/data1",
"https://api.example.com/data2",
"https://api.example.com/data3",
]
def fetch_url(url):
print(f"Starting fetch: {url}")
time.sleep(1) # Simulate network response delay
return f"Data from {url}"
# Run downloads concurrently across 3 worker threads
start_time = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(fetch_url, urls))
elapsed = time.time() - start_time
print(f"Fetched {len(results)} URLs concurrently in {elapsed:.2f} seconds!")
Example 2 — Thread Safety with Lock
python
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
# Acquire lock to prevent race conditions
with lock:
counter += 1
threads = [threading.Thread(target=increment) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()
print("Final Counter (Race Condition Free):", counter)
Visual Flow
The following execution flow represents multithreaded I/O execution:
⚡ Visual Execution Flow
Launch Worker Thread Pool→Thread 1 Waits on I/O (GIL Released)→Thread 2 Executes Concurrently→Gather Future Results
Common Mistakes
- Using Threads for Heavy Math: Expecting multithreading to speed up CPU-bound tasks like matrix multiplication. Use multiprocessing for CPU tasks.
- Race Conditions: Modifying shared global data across threads without thread locks (
threading.Lock).
Best Practices
- Always use
concurrent.futures.ThreadPoolExecutorcontext managers for thread management.