CodeNebula
Python⚡Concurrencyintermediate

`threading` — OS-Level Threads

Python · Concurrency · Run with python3

Explanation

`threading.Thread` runs a callable in an OS thread. Threads share the process's memory — communication is via shared objects, protected by `threading.Lock`, `RLock`, `Semaphore`, `Event`, `Condition`, and `Barrier`. `threading.local()` provides thread-local storage. Threads are well-suited for I/O-bound concurrency: database queries, HTTP requests, file I/O. A `Daemon` thread dies automatically when the main thread exits.

Real-World Scenario

A web scraper fetches 100 URLs concurrently using 20 threads. Each thread blocks on network I/O — the GIL is released during `socket.recv`, so threads truly run concurrently at the network level. Total time ≈ max single-URL latency, not sum. ```python import threading import time import random from queue import Queue results: Queue[tuple[str, int]] = Queue() def fetch(url: str) -> None: """Simulates an HTTP request with random latency.""" time.sleep(random.uniform(0.05, 0.3)) status = 200 if random.random() > 0.1 else 500 results.put((url, status)) urls = [f"https://api.example.com/item/{i}" for i in range(10)] # Thread pool via list start = time.perf_counter() threads = [threading.Thread(target=fetch, args=(u,)) for u in urls] for t in threads: t.start() for t in threads: t.join() elapsed = time.perf_counter() - start print(f"Fetched {results.qsize()} URLs in {elapsed:.2f}s") successes = sum(1 for _ in range(results.qsize()) if results.get()[1] == 200) ```

Quick Snippet

snippet.py
import threading
import time
import random
from queue import Queue

results: Queue[tuple[str, int]] = Queue()

def fetch(url: str) -> None:
    """Simulates an HTTP request with random latency."""
    time.sleep(random.uniform(0.05, 0.3))
    status = 200 if random.random() > 0.1 else 500
    results.put((url, status))

urls = [f"https://api.example.com/item/{i}" for i in range(10)]

# Thread pool via list
start = time.perf_counter()
threads = [threading.Thread(target=fetch, args=(u,)) for u in urls]
for t in threads: t.start()
for t in threads: t.join()
elapsed = time.perf_counter() - start

print(f"Fetched {results.qsize()} URLs in {elapsed:.2f}s")
successes = sum(1 for _ in range(results.qsize()) if results.get()[1] == 200)

Full Example

threading_basics.py
# Compile: python3 threading_basics.py
# Output:
# Fetched 10 URLs

import threading
import time
import random
from queue import Queue

results: Queue = Queue()

def fetch(url: str) -> None:
    time.sleep(random.uniform(0.01, 0.05))
    status = 200 if random.random() > 0.1 else 500
    results.put((url, status))

urls = [f"https://api.example.com/item/{i}" for i in range(10)]

start = time.perf_counter()
threads = [threading.Thread(target=fetch, args=(u,)) for u in urls]
for t in threads: t.start()
for t in threads: t.join()
elapsed = time.perf_counter() - start

fetched = []
while not results.empty():
    fetched.append(results.get())

print(f"Fetched {len(fetched)} URLs in {elapsed:.3f}s")
successes = sum(1 for _, s in fetched if s == 200)
print(f"Success: {successes}/{len(fetched)}")

# Thread-local storage
local = threading.local()

def worker(name: str) -> None:
    local.name = name
    time.sleep(0.01)
    print(f"Thread sees: {local.name}")

threads = [threading.Thread(target=worker, args=(f"worker-{i}",)) for i in range(3)]
for t in threads: t.start()
for t in threads: t.join()
Run command
python3 threading_basics.py