Threading & GIL
หมายเหตุเกี่ยวกับ Playground:
threadingต้องการการสร้าง OS thread — browser sandbox ไม่สามารถทำได้ รัน code snippets ในเครื่องด้วยpython3component<TsGo>,<Quiz>และ<ProgressTracker>ยังคงทำงานได้ตามปกติ
Thread vs event loop
หัวข้อที่มีชื่อว่า “Thread vs event loop”JavaScript ให้คุณ model เดียวสำหรับงาน concurrent I/O: single-threaded event loop กับ async/await สำหรับงาน CPU-heavy คุณ spawn Worker ซึ่ง run บน thread ที่แยกต่างหากโดยสิ้นเชิงพร้อม memory ของตัวเอง — ไม่มี shared state โดย default
threading module ของ Python สร้าง OS thread ที่ แชร์ memory space เดียวกัน ทำให้การส่งข้อมูลระหว่าง thread ง่าย แต่ยังหมายความว่าคุณต้องปกป้อง shared data ด้วย lock เพื่อหลีกเลี่ยง race condition
// TypeScript — Web Worker (separate memory, message passing)const worker = new Worker(new URL("./worker.ts", import.meta.url));worker.postMessage({ numbers: [1, 2, 3, 4, 5] });worker.onmessage = (e) => console.log("result:", e.data);
// Inside worker.ts:self.onmessage = (e) => { const sum = e.data.numbers.reduce((a: number, b: number) => a + b, 0); self.postMessage(sum);};# Python — threads share memory directlyimport threading
results: list[int] = []lock = threading.Lock()
def worker(numbers: list[int]) -> None: total = sum(numbers) with lock: # protect shared state results.append(total)
t = threading.Thread(target=worker, args=([1, 2, 3, 4, 5],))t.start()t.join() # wait for thread to finishprint(results) # [15]Threading พื้นฐาน
หัวข้อที่มีชื่อว่า “Threading พื้นฐาน”import threadingimport time
def download(url: str) -> None: print(f"Downloading {url}...") time.sleep(1) # simulate I/O wait print(f"Done: {url}")
urls = [ "https://api.example.com/users", "https://api.example.com/posts", "https://api.example.com/tags",]
threads = [threading.Thread(target=download, args=(url,)) for url in urls]
for t in threads: t.start() # start all threads
for t in threads: t.join() # wait for all to finish
print("All downloads complete")รัน code นี้ในเครื่อง — browser sandbox ไม่สามารถ spawn OS thread/process ได้
Thread กับ return value
หัวข้อที่มีชื่อว่า “Thread กับ return value”Thread ไม่ return value โดยตรง (ต่างจาก async/await) pattern ที่พบบ่อยคือใช้ shared list หรือ queue.Queue:
import threadingimport queue
def fetch_data(label: str, result_queue: queue.Queue) -> None: # simulate work import time; time.sleep(0.1) result_queue.put(f"data from {label}")
q: queue.Queue[str] = queue.Queue()
threads = [ threading.Thread(target=fetch_data, args=(label, q)) for label in ["users", "posts", "tags"]]for t in threads: t.start()for t in threads: t.join()
results = [q.get() for _ in threads]print(results)เมื่อไหร่ควรใช้ threading
หัวข้อที่มีชื่อว่า “เมื่อไหร่ควรใช้ threading”// TypeScript — you rarely think about this distinction because// JS is always single-threaded. Workers are for any heavy work.// asyncio/Promises handle all I/O natively.# Python — choose the right tool# Use threading when:# - Wrapping a blocking library that has no async version# (e.g., an old database driver, a blocking SDK)# - The task is I/O-bound (file reads, network calls, DB queries)# - You need shared memory between tasks (vs multiprocessing)
# Prefer asyncio when:# - You control the code and can use async libraries# - You need thousands of concurrent I/O tasks# (threads have OS overhead; coroutines are cheap)
# Use multiprocessing when:# - The task is CPU-bound (math, image processing, ML inference)Thread safety และ locks
หัวข้อที่มีชื่อว่า “Thread safety และ locks”import threading
counter = 0lock = threading.Lock()
def safe_increment() -> None: global counter with lock: # acquire on enter, release on exit (even if exception) counter += 1
threads = [threading.Thread(target=safe_increment) for _ in range(100)]for t in threads: t.start()for t in threads: t.join()
print(counter) # always 100 — lock prevents race condition