Node.js Worker Threads:處理高負載 CPU 運算的解決方案
長期以來,「單執行緒」一直是 Node.js 的核心標籤。雖然這在處理 I/O 密集型任務(如網頁伺服器)時極具優勢,但當遇到極其耗費 CPU 的運算(如圖片轉檔、複雜加密、大型數值模擬)時,主執行緒會被卡死,導致所有請求停擺。Worker Threads 模組正是為了打破這個宿命而生。
為什麼需要 Worker Threads?
在 Node.js 中,如果你的程式碼包含一個耗時 5 秒的同步計算,這 5 秒內 Node.js 無法處理任何非同步事件(如 HTTP 請求)。Worker Threads 允許你開啟額外的執行緒,利用多核心 CPU 的優勢,在背景進行運算而不干擾主執行緒的運行。
基礎實戰範例
我們通常會在程式中判斷目前是「主執行緒」還是「工作者執行緒」:
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
// --- 1. 主執行緒邏輯 ---
console.log('主執行緒:開始委派重運算任務...');
const worker = new Worker(__filename, {
workerData: { num: 42 }, // 傳遞參數給工作者
});
worker.on('message', (result) => {
console.log('成品回來了!計算結果:', result);
});
worker.on('error', (err) => console.error('工作出錯了:', err));
} else {
// --- 2. 工作者執行緒邏輯 ---
const heavyResult = fibonacci(workerData.num); // 執行耗時計算
parentPort.postMessage(heavyResult); // 將成果送回主執行緒
}
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Worker Threads vs Child Process
雖然兩者都能避開主執行緒阻塞,但底層設計完全不同:
| 特性 | Child Process (子進程) | Worker Threads (工作執行緒) |
|---|---|---|
| 資源消耗 | 高 (獨立的 OS 程序) | 低 (同進程內的輕量執行緒) |
| 通訊成本 | 高 (資料需序列化/跨進程) | 低 (可共享記憶體) |
| 記憶體 | 隔離 (空間不互通) | 共享 (支援 SharedArrayBuffer) |
| 適用場景 | 執行外部 shell 指令 | 純 JavaScript 重度計算 |
效能殺手鐧:共享記憶體 (Shared Memory)
Worker Threads 最強大的地方在於支援 SharedArrayBuffer。這讓多個執行緒可以同時讀寫同一塊記憶體區域,而不需要透過「訊息傳遞 (Message Passing)」進行大量的資料複製,這在處理海量數據運算時效能極佳。
小提醒:操作共享記憶體時需要使用
Atomics 模組,以避免發生競態條件 (Race Condition) 導致資料損毀。總結
- 不要濫用:開啟執行緒是有開銷的。若任务是非同步 I/O(如讀檔案、查資料庫),絕對不要用 Worker Threads,用原生非同步才是王道。
- 適用場景:涉及大量迴圈、矩陣運算、加密解密、解析極大型 JSON 或圖片處理。
- 它讓 Node.js 終於具備了與傳統多執行緒語言(如 Java, C#)一較高下的運算能力。