concurrent.futures
หมายเหตุเกี่ยวกับ Playground:
concurrent.futuresspawn OS thread/process — browser sandbox ไม่สามารถทำได้ รัน code snippets ในเครื่องด้วยpython3component<TsGo>,<Quiz>และ<ProgressTracker>ยังคงทำงานได้ตามปกติ
High-level API
หัวข้อที่มีชื่อว่า “High-level API”concurrent.futures คือคำตอบของ Python สำหรับโจทย์ “อยากได้ thread หรือ process แต่ไม่อยาก manage เอง” โดยให้ API ชุดเดียวที่สะอาดและให้ความรู้สึกใกล้เคียงโมเดล Promise ของ JavaScript มากกว่าการเล่นกับ threading หรือ multiprocessing ดิบ ๆ
executor ทั้งสองเป็น drop-in replacement สำหรับกัน — คุณสลับระหว่าง thread-based และ process-based concurrency โดยเปลี่ยนแค่หนึ่งบรรทัด
// TypeScript — Promise.all over an array (built-in)const tasks = [1, 2, 3, 4, 5].map((n) => fetch(`/api/item/${n}`).then((r) => r.json()));const results = await Promise.all(tasks);
// No need for explicit thread pools — JS handles it# Python — concurrent.futures is the closest equivalentfrom concurrent.futures import ThreadPoolExecutor
def fetch_item(n: int) -> dict: import time; time.sleep(0.1) # simulate I/O return {"id": n, "value": n * 2}
with ThreadPoolExecutor(max_workers=5) as executor: # executor.map — like Promise.all over an array results = list(executor.map(fetch_item, [1, 2, 3, 4, 5]))
print(results)ThreadPoolExecutor — งาน I/O-bound
หัวข้อที่มีชื่อว่า “ThreadPoolExecutor — งาน I/O-bound”from concurrent.futures import ThreadPoolExecutor, as_completedimport time
def download(url: str) -> str: time.sleep(0.1) # simulate network I/O return f"Downloaded: {url}"
urls = [ "https://api.example.com/users", "https://api.example.com/posts", "https://api.example.com/tags",]
# executor.map — results in INPUT order (like Promise.all)with ThreadPoolExecutor(max_workers=3) as executor: for result in executor.map(download, urls): print(result)
print("---")
# executor.submit + as_completed — results in COMPLETION orderwith ThreadPoolExecutor(max_workers=3) as executor: futures = {executor.submit(download, url): url for url in urls} for future in as_completed(futures): url = futures[future] print(f"{url} → {future.result()}")รัน code นี้ในเครื่อง — browser sandbox ไม่สามารถ spawn OS thread/process ได้
ProcessPoolExecutor — งาน CPU-bound
หัวข้อที่มีชื่อว่า “ProcessPoolExecutor — งาน CPU-bound”from concurrent.futures import ProcessPoolExecutor
def is_prime(n: int) -> bool: if n < 2: return False for i in range(2, int(n ** 0.5) + 1): if n % i == 0: return False return True
if __name__ == "__main__": numbers = list(range(1_000_000, 1_000_100))
# ThreadPoolExecutor: limited by GIL — no speedup for CPU work # ProcessPoolExecutor: each process has its own GIL → real parallelism with ProcessPoolExecutor(max_workers=4) as executor: primes = [n for n, p in zip(numbers, executor.map(is_prime, numbers)) if p] print(primes[:5])Future objects — ตรวจสอบ individual results
หัวข้อที่มีชื่อว่า “Future objects — ตรวจสอบ individual results”from concurrent.futures import ThreadPoolExecutor, Futureimport time
def task(n: int) -> str: time.sleep(n * 0.01) return f"result-{n}"
with ThreadPoolExecutor(max_workers=3) as executor: f1: Future[str] = executor.submit(task, 1) f2: Future[str] = executor.submit(task, 2) f3: Future[str] = executor.submit(task, 3)
# .result() blocks until done — like await promise print(f1.result()) # "result-1" print(f2.result()) # "result-2"
# Check state without blocking print(f3.done()) # True/False print(f3.running()) # True/Falseการเลือกระหว่าง ThreadPoolExecutor และ ProcessPoolExecutor
หัวข้อที่มีชื่อว่า “การเลือกระหว่าง ThreadPoolExecutor และ ProcessPoolExecutor”// TypeScript// No explicit choice needed — the event loop handles I/O,// Web Workers handle CPU. The runtime decides threading.# Python — you choose explicitlyfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# I/O-bound (network, disk, DB calls with blocking libraries)with ThreadPoolExecutor(max_workers=20) as executor: results = list(executor.map(blocking_http_call, urls))
# CPU-bound (math, compression, parsing, ML inference)with ProcessPoolExecutor(max_workers=4) as executor: results = list(executor.map(cpu_intensive_fn, data))
# Rule of thumb for max_workers:# Threads: 5 * os.cpu_count() (I/O-bound — threads mostly sleep)# Processes: os.cpu_count() (CPU-bound — one per core is enough)