# 事件迴圈不是多執行緒：Node.js 併發最常見的誤會

> Node.js 的非阻塞 I/O 看起來很像多執行緒，但骨子裡是單執行緒事件迴圈，這一字之差在 CPU 密集場景會踩到很痛的天花板。

原文：https://sheng.page/posts/node-event-loop-is-not-multithreading/ · 發布：2025-06-21 · 作者：Sheng

Node 能同時處理幾萬個連線，常被誤解成多執行緒。它的併發其實靠單執行緒事件迴圈搭配非阻塞 I/O，runtime 主要只有一條執行緒在跑；讀檔、網路請求與資料庫查詢等待期間不佔用 CPU，loop 才能先處理其他事件，等 I/O 完成再回來繼續。這種「同時」是併發，並非真正的平行運算。

心智模型我通常這樣講：把 event loop 想成一個很快的服務生，在餐廳裡輪流招呼很多桌，每桌點完菜就去下一桌。問題出在哪？如果有一桌點了需要現炒 30 分鐘的菜，而且他站在那邊等，後面所有桌全都卡住。I/O 密集的場景（API gateway、WebSocket fan-out、proxy），服務生很少需要等，轉得飛快；CPU 密集的計算（影像處理、加密、遊戲伺服器即時運算），就是那道現炒，炒完之前誰都別想要菜。

`worker_threads` 確實可以把重 CPU 工作丟到另一條執行緒，但那不是不花成本的：序列化開銷、訊息傳遞延遲、記憶體隔離的限制，都是代價。這讓你得額外付出設計成本，即便 Node 依然能處理 CPU 工作。這也是為什麼有些後端我會選別的語言或 runtime：純粹是場景不對。一個需要即時物理運算的遊戲伺服器，選 Node 然後用 `worker_threads` 硬兜，不如一開始就用更適合的工具；一個以 I/O 為主、需要快速迭代的 API 服務，Node 幾乎沒有理由不用。

選型的本質是承認工具有邊界，然後選對場景，不是找「最強的」然後硬塞所有問題進去。


## 補充筆記

- Node.js 的高並發靠非阻塞 I/O 加事件迴圈，不是多執行緒，主執行緒始終只有一條。
- I/O 密集場景是最適場景，CPU 密集會直接把整條 event loop 堵住，影響所有連線。
- `worker_threads` 能補 CPU 工作，但不是零成本，序列化與訊息傳遞都有額外設計負擔。
- 「能 build」不等於「場景合適」，選型前先確認工作負載的性質，再選工具。
- 事件迴圈心智模型：一個服務生、很多桌、一桌現炒、全場等。
> 想法與技術判斷出自 Sheng，和 Claude 一起起草 · 列入 20260613 blog 翻新計劃，新漆未乾。