🚀
Advanced
Thread · Process · Lock · Queue · concurrent.futures
Week 4 — Threading & Multiprocessing
Write concurrent programs with threading and multiprocessing. Understand the GIL, synchronize shared state with Lock and Queue, and use concurrent.futures for a clean high-level API.
threadingmultiprocessingGILLockconcurrent.futures
Duration
⏱ 3 hours
Level
📊 Advanced
Prerequisite
🎯 Intermediate Weeks 1–3
OUTCOME
Build a parallel image downloader and a multi-process number cruncher
What you'll learn
- 1Explain the GIL and when threading vs multiprocessing is appropriate
- 2Create and join threads with threading.Thread
- 3Synchronize shared state with Lock
- 4Use Queue for producer-consumer patterns
- 5Speed up CPU-bound tasks with ProcessPoolExecutor
1. Threading
The GIL allows only one thread to execute Python bytecode at a time. Threading is best for I/O-bound tasks (network, disk).
python
import threading, time
def fetch(url, results, idx):
time.sleep(0.5) # simulate network I/O
results[idx] = f"data from {url}"
urls = ["http://a.com", "http://b.com", "http://c.com"]
results = [None] * len(urls)
threads = [threading.Thread(target=fetch, args=(u, results, i)) for i, u in enumerate(urls)]
for t in threads: t.start()
for t in threads: t.join()
print(results)2. Lock & Queue
python
import threading
from queue import Queue
lock = threading.Lock()
counter = 0
def increment(n):
global counter
for _ in range(n):
with lock:
counter += 1
t1 = threading.Thread(target=increment, args=(10000,))
t2 = threading.Thread(target=increment, args=(10000,))
t1.start(); t2.start()
t1.join(); t2.join()
print(counter) # always 20000 with lock3. concurrent.futures
python
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# I/O-bound: use ThreadPoolExecutor
def download(url):
time.sleep(0.5) # simulate
return f"result:{url}"
with ThreadPoolExecutor(max_workers=4) as executor:
futures = [executor.submit(download, f"http://site{i}.com") for i in range(8)]
results = [f.result() for f in futures]
print(len(results), "downloads done")
# CPU-bound: use ProcessPoolExecutor
def is_prime(n):
if n < 2: return False
return all(n % i for i in range(2, int(n**0.5)+1))
with ProcessPoolExecutor() as executor:
primes = list(executor.map(is_prime, range(1000)))💻 Examples
Run these examples and check the output yourself.
01_thread_pool.py— Parallel URL checker using ThreadPoolExecutor
CODE
from concurrent.futures import ThreadPoolExecutor, as_completed
import urllib.request, time
URLs = [
"https://www.python.org",
"https://docs.python.org",
"https://pypi.org",
]
def check_url(url):
try:
start = time.perf_counter()
urllib.request.urlopen(url, timeout=5)
return url, True, time.perf_counter() - start
except Exception as e:
return url, False, 0
with ThreadPoolExecutor(max_workers=len(URLs)) as ex:
futures = {ex.submit(check_url, u): u for u in URLs}
for future in as_completed(futures):
url, ok, elapsed = future.result()
status = "OK" if ok else "FAIL"
print(f"[{status}] {url} ({elapsed:.2f}s)")
📝 Exercises
Try them yourself first, then open the solution to compare.
Exercise 1
Producer-Consumer Queue
Goal: Implement a producer-consumer pattern with threading.Queue.
Requirements
- 1 producer thread generates 20 work items
- 4 consumer threads process items (simulate with sleep)
- Use Queue to safely pass items
- Print when each item is produced and consumed
▶Toggle solution
SOLUTION
import threading, queue, time, random
work_q = queue.Queue()
def producer():
for i in range(20):
item = f"item-{i:02d}"
work_q.put(item)
print(f" Produced: {item}")
time.sleep(random.uniform(0.05, 0.1))
for _ in range(4): # sentinel values
work_q.put(None)
def consumer(cid):
while True:
item = work_q.get()
if item is None:
work_q.task_done()
break
time.sleep(random.uniform(0.1, 0.3))
print(f" Consumer-{cid}: processed {item}")
work_q.task_done()
threads = [threading.Thread(target=producer)]
threads += [threading.Thread(target=consumer, args=(i,)) for i in range(4)]
for t in threads: t.start()
for t in threads: t.join()
print("All done")
Example code / lecture materials
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗