Threads: Best for I/O-Bound Tasks
Threads are small workers that run inside the same Python process and share memory. They’re lightweight, easy to create, and ideal when work is mostly waiting.
Imagine downloading ten web pages. Suppose each download spends most of its time waiting for the server to respond. Threads can overlap this waiting time, finishing the job much earlier.
Real-World Analogy
A single cook in a kitchen needs to wait while water boils. But if you hire several assistants, they can: - set the table - fetch ingredients - prepare sauces
…all while the water still boils.
This is exactly how threads hide waiting time.
Example: Parallel “Network” Requests Using Threads
This example simulates network calls using time.sleep().
If each task waits, threads help overlap that waiting.
pool.submit schedules a function and returns a future, an object representing its eventual result. as_completed supplies completed futures; result() retrieves a value or raises the task’s exception.
Code Example
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
def fake_fetch(url, delay=0.2):
"""Simulate a slow network request by sleeping briefly."""
time.sleep(delay)
return f"OK {url} ({delay:.1f}s)"
urls = [f"https://example.org/page/{i}" for i in range(5)]
start = time.perf_counter()
# Create a pool of threads
with ThreadPoolExecutor(max_workers=5) as pool:
# Schedule each fetch in its own thread
futures = [
pool.submit(fake_fetch, u, 0.2 + (i % 3) * 0.1)
for i, u in enumerate(urls)
]
# Print results as they finish
for fut in as_completed(futures):
print(fut.result())
elapsed = time.perf_counter() - start
print(f"Total time ~ {elapsed:.2f}s (overlapping waits)")What’s Happening?
- Each call to
fake_fetch()sleeps for a moment (like waiting on a slow network). - The thread “releases” the CPU during sleep.
- Another thread immediately starts its work.
- All threads overlap their waiting time, so the whole job finishes quickly.
Change max_workers=5 to max_workers=1 in the executor call and run the complete program again.
Now the script must complete each request sequentially—much slower.
This is the heart of concurrency: hiding wait time.
Completion order and measured time can vary; these are simulated waits, not HTTP requests. The next section changes the workload from waiting to computing.