事件迴圈不是多執行緒:Node.js 併發最常見的誤會
Just a void 學海無涯
JavaScript 事件迴圈不是多執行緒:Node.js 併發最常見的誤會
有個誤會在 Node.js 社群裡流傳很久:「Node 能同時處理幾萬個連線,所以它是多執行緒的。」不,它不是。Node 的併發靠的是單執行緒事件迴圈搭配非阻塞 I/O,整個 runtime 骨子裡就一條主執行緒在跑。會覺得「同時」,是因為 I/O 操作(讀檔、網路請求、資料庫查詢)在等待期間不佔用 CPU,loop 可以去處理別的事件,等 I/O 完成再回來繼續,不是真正的並行。
心智模型我通常這樣講:把 event loop 想成一個很快的服務生,在餐廳裡輪流招呼很多桌,每桌點完菜就去下一桌。問題出在哪?如果有一桌點了需要現炒 30 分鐘的菜,而且他站在那邊等,後面所有桌全都卡住。I/O 密集的場景(API gateway、WebSocket fan-out、proxy),服務生很少需要等,轉得飛快;CPU 密集的計算(影像處理、加密、遊戲伺服器即時運算),就是那道現炒,炒完之前誰都別想要菜。
worker_threads 確實可以把重 CPU 工作丟到另一條執行緒,但那不是不花成本的:序列化開銷、訊息傳遞延遲、記憶體隔離的限制,都是代價。這不代表 Node 不能處理 CPU 工作,而是你要額外付出設計成本。這也是為什麼有些後端我會選別的語言或 runtime:不是 JS 不好,是場景不對。一個需要即時物理運算的遊戲伺服器,選 Node 然後用 worker_threads 硬兜,不如一開始就用更適合的工具;一個以 I/O 為主、需要快速迭代的 API 服務,Node 幾乎沒有理由不用。
選型的本質是承認工具有邊界,然後選對場景,不是找「最強的」然後硬塞所有問題進去。
重點整理
- Node.js 的高並發靠非阻塞 I/O 加事件迴圈,不是多執行緒,主執行緒始終只有一條。
- I/O 密集場景是最適場景,CPU 密集會直接把整條 event loop 堵住,影響所有連線。
worker_threads能補 CPU 工作,但不是零成本,序列化與訊息傳遞都有額外設計負擔。- 「能 build」不等於「場景合適」,選型前先確認工作負載的性質,再選工具。
- 事件迴圈心智模型:一個服務生、很多桌、一桌現炒、全場等。
觀點 Sheng,內文 Claude 協助 · 列入 20260613 blog 翻新計劃,新漆未乾。