ข้ามไปยังเนื้อหา

concurrent.futures

หมายเหตุเกี่ยวกับ Playground: concurrent.futures spawn OS thread/process — browser sandbox ไม่สามารถทำได้ รัน code snippets ในเครื่องด้วย python3 component <TsGo>, <Quiz> และ <ProgressTracker> ยังคงทำงานได้ตามปกติ

concurrent.futures คือคำตอบของ Python สำหรับโจทย์ “อยากได้ thread หรือ process แต่ไม่อยาก manage เอง” โดยให้ API ชุดเดียวที่สะอาดและให้ความรู้สึกใกล้เคียงโมเดล Promise ของ JavaScript มากกว่าการเล่นกับ threading หรือ multiprocessing ดิบ ๆ

executor ทั้งสองเป็น drop-in replacement สำหรับกัน — คุณสลับระหว่าง thread-based และ process-based concurrency โดยเปลี่ยนแค่หนึ่งบรรทัด

TypeScript
// 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
# Python — concurrent.futures is the closest equivalent
from 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)
from concurrent.futures import ThreadPoolExecutor, as_completed
import 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 order
with 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 ได้

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])
from concurrent.futures import ThreadPoolExecutor, Future
import 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
TypeScript
// TypeScript
// No explicit choice needed — the event loop handles I/O,
// Web Workers handle CPU. The runtime decides threading.
Python
# Python — you choose explicitly
from 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)
ข้อได้เปรียบหลักของ `concurrent.futures` เทียบกับ raw `threading` หรือ `multiprocessing` คืออะไร?
ความแตกต่างระหว่าง `executor.map()` และ `as_completed()` คืออะไร?
ควรใช้ executor ใดสำหรับงาน CPU-bound เพื่อให้ได้ parallelism จริงๆ?
จะ run blocking synchronous function ภายใน asyncio event loop โดยไม่ block loop ได้อย่างไร?