GIL — Global Interpreter Lock
JavaScript ใช้ single thread, Python มี thread แต่มี GIL
หัวข้อที่มีชื่อว่า “JavaScript ใช้ single thread, Python มี thread แต่มี GIL”JavaScript ทำงานใน event loop single-threaded โดยการออกแบบ ความสามารถด้าน concurrency ทั้งหมดมาจาก asynchrony (async/await, Promise, callback) ไม่ใช่การประมวลผลพร้อมกันแบบขนาน ถ้าคุณต้องการใช้ CPU หลายตัวจริง ๆ คุณต้องใช้ worker_threads
Python มี thread แต่ CPython (implementation หลัก) มี lock ชื่อว่า Global Interpreter Lock (GIL) ที่อนุญาตให้ Python bytecode ทำงานได้ครั้งละหนึ่ง thread เท่านั้น ซึ่งหมายความว่า thread ของ Python ใน CPython ไม่สามารถรัน CPU-bound code แบบขนานได้จริง ๆ แม้บนเครื่องที่มีหลาย CPU
GIL ไม่ได้หมายความว่า threading ไร้ประโยชน์
หัวข้อที่มีชื่อว่า “GIL ไม่ได้หมายความว่า threading ไร้ประโยชน์”Python ปล่อย GIL ออกระหว่างทำ I/O, ระหว่าง time.sleep() และระหว่างงานใน C extension บางตัวอย่าง NumPy ดังนั้นถ้าเป็น I/O-bound concurrency เช่น เรียก network, อ่านไฟล์ หรือ query database thread ของ Python ทำงานได้ดีมาก
// TypeScript: worker_threads for CPU-bound parallelismimport { Worker, isMainThread, parentPort } from "worker_threads";
if (isMainThread) { const worker = new Worker(__filename); worker.on("message", (result) => { console.log("result from worker:", result); }); worker.postMessage("start");} else { parentPort?.on("message", () => { // CPU-bound work runs in a real OS thread let total = 0; for (let i = 0; i < 1_000_000; i++) total += i; parentPort?.postMessage(total); });}# Python: threading is effective for I/O-bound work# but the GIL limits CPU-bound parallelism in CPythonimport threading
results = {}
def fetch_data(key, value): # Simulate I/O work (GIL is released during real I/O) import time time.sleep(0.1) results[key] = value * 2
threads = [ threading.Thread(target=fetch_data, args=(i, i)) for i in range(5)]for t in threads: t.start()for t in threads: t.join()
print(results) # {0: 0, 1: 2, 2: 4, 3: 6, 4: 8}เมื่อต้องการ parallelism จริง ๆ ใช้ multiprocessing
หัวข้อที่มีชื่อว่า “เมื่อต้องการ parallelism จริง ๆ ใช้ multiprocessing”สำหรับงาน CPU-bound ที่ต้องการใช้ CPU หลายตัวจริง ๆ Python มี module multiprocessing ซึ่งแต่ละ worker จะเป็น OS process แยกต่างหากพร้อม Python interpreter ของตัวเอง ทำให้แต่ละ process มี GIL ของตัวเองด้วย
// TypeScript: CPU parallelism via worker_threads poolimport { Worker } from "worker_threads";import * as os from "os";
function runWorker(data: number): Promise<number> { return new Promise((resolve, reject) => { const w = new Worker(` const { parentPort, workerData } = require("worker_threads"); let total = 0; for (let i = 0; i < workerData; i++) total += i; parentPort.postMessage(total); `, { eval: true, workerData: data }); w.on("message", resolve); w.on("error", reject); });}
const cpus = os.cpus().length;Promise.all(Array.from({ length: cpus }, (_, i) => runWorker(1_000_000 / cpus))).then(results => console.log(results.reduce((a, b) => a + b, 0)));# Python: multiprocessing bypasses the GIL entirelyfrom multiprocessing import Poolimport os
def cpu_work(n): return sum(range(n))
if __name__ == "__main__": cpus = os.cpu_count() chunk = 1_000_000 // cpus with Pool(cpus) as pool: results = pool.map(cpu_work, [chunk] * cpus) print(sum(results))หมายเหตุ: Playground ไม่พร้อมใช้งานสำหรับบทเรียนนี้ เนื่องจากตัวอย่าง multiprocessing ต้องการ if __name__ == "__main__" guard และรันได้ถูกต้องเฉพาะในสภาพแวดล้อม process แบบ fork เท่านั้น