Home > Blog > tech

Worker Threads คืออะไร? สอนใช้ Multi-threading ใน Node.js สำหรับ CPU-intensive Tasks 2026

worker threads nodejs guide
Worker Threads Node.js Guide 2026
2026-04-16 | tech | 3400 words

Node.js ขึ้นชื่อเรื่องความเร็วในการจัดการ I/O-bound Tasks ด้วย Event Loop แบบ Single-threaded แต่เมื่อเจองานหนักที่ใช้ CPU มาก (CPU-intensive Tasks) เช่น การประมวลผลรูปภาพ การ Parse ข้อมูลขนาดใหญ่ หรือการคำนวณ Cryptography Event Loop จะถูก Block ทำให้ Server ไม่ตอบสนอง Worker Threads คือทางออกของปัญหานี้

ปัญหาของ Node.js Single Thread

Node.js ใช้ V8 JavaScript Engine ที่ทำงานบน Thread เดียว ซึ่งดีสำหรับ I/O Operations (อ่านไฟล์ เรียก API Query Database) เพราะ Node.js จะไม่รอ I/O แต่จะไปทำงานอื่นก่อนแล้วกลับมาเมื่อ I/O เสร็จ (Non-blocking I/O)

แต่ถ้ามีงาน CPU-intensive เช่น Loop คำนวณ 1 ล้านรอบ Event Loop จะถูก Block ทั้งหมด ไม่มีใครใช้งาน Server ได้จนกว่าจะคำนวณเสร็จ

// ตัวอย่าง: CPU-intensive task ที่ Block Event Loop
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

// ถ้า n = 45 จะใช้เวลาหลายวินาที
// ระหว่างนั้น Server จะไม่ตอบสนองเลย!
app.get('/fibonacci', (req, res) => {
  const result = fibonacci(45); // Block Event Loop!
  res.json({ result });
});

Worker Threads คืออะไร?

Worker Threads คือ Module ในตัวของ Node.js (worker_threads) ที่ให้คุณสร้าง Thread ใหม่เพื่อรัน JavaScript Code แบบขนาน (Parallel) กับ Main Thread ทำให้งาน CPU-intensive ไม่ Block Event Loop

// main.js — สร้าง Worker
const { Worker } = require('worker_threads');

const worker = new Worker('./worker.js', {
  workerData: { n: 45 }
});

worker.on('message', (result) => {
  console.log('Fibonacci result:', result);
});

worker.on('error', (err) => {
  console.error('Worker error:', err);
});

worker.on('exit', (code) => {
  console.log('Worker exited with code:', code);
});
// worker.js — ทำงานใน Thread แยก
const { workerData, parentPort } = require('worker_threads');

function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

const result = fibonacci(workerData.n);
parentPort.postMessage(result); // ส่งผลกลับ Main Thread

การส่งข้อมูลระหว่าง Threads

1. postMessage (Structured Clone)

// Main Thread
worker.postMessage({ type: 'calculate', data: [1, 2, 3, 4, 5] });

// Worker Thread
parentPort.on('message', (msg) => {
  if (msg.type === 'calculate') {
    const sum = msg.data.reduce((a, b) => a + b, 0);
    parentPort.postMessage({ type: 'result', sum });
  }
});
หมายเหตุ: postMessage ใช้ Structured Clone Algorithm ซึ่ง Copy ข้อมูลทั้งหมด ถ้าข้อมูลใหญ่มากจะช้า ใช้ SharedArrayBuffer หรือ transferList แทน

2. SharedArrayBuffer — แชร์หน่วยความจำ

// Main Thread — สร้าง SharedArrayBuffer
const sharedBuffer = new SharedArrayBuffer(1024); // 1KB
const arr = new Int32Array(sharedBuffer);
arr[0] = 42;

const worker = new Worker('./worker.js', {
  workerData: { sharedBuffer }
});

// Worker Thread — อ่าน/เขียน SharedArrayBuffer ได้เลย
const { workerData } = require('worker_threads');
const arr = new Int32Array(workerData.sharedBuffer);
console.log(arr[0]); // 42
arr[1] = 100; // Main Thread จะเห็นค่านี้ด้วย

3. transferList — โอนย้ายข้อมูล (Zero-copy)

// Main Thread — Transfer ArrayBuffer (ไม่ copy, ย้ายเลย)
const buffer = new ArrayBuffer(1024);
const uint8 = new Uint8Array(buffer);
uint8[0] = 255;

worker.postMessage({ buffer }, [buffer]);
// buffer ใน Main Thread จะใช้ไม่ได้อีกต่อไป (transferred)

Worker Pool Pattern

การสร้าง Worker ใหม่ทุกครั้งมี Overhead ทางออกคือสร้าง Pool ของ Workers ที่พร้อมใช้งาน

ใช้ Library: piscina

// npm install piscina
const Piscina = require('piscina');

const pool = new Piscina({
  filename: './worker.js',
  maxThreads: 4, // จำนวน Worker สูงสุด
  minThreads: 2, // จำนวน Worker ขั้นต่ำ
});

// ใช้งาน — Pool จัดการให้อัตโนมัติ
async function processAll() {
  const results = await Promise.all([
    pool.run({ n: 40 }),
    pool.run({ n: 41 }),
    pool.run({ n: 42 }),
    pool.run({ n: 43 }),
  ]);
  console.log(results);
}
// worker.js สำหรับ piscina
module.exports = function({ n }) {
  function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
  return fibonacci(n);
};

ใช้ Library: workerpool

// npm install workerpool
const workerpool = require('workerpool');

const pool = workerpool.pool('./worker.js', {
  maxWorkers: 4,
});

pool.exec('fibonacci', [42])
  .then(result => console.log(result))
  .catch(err => console.error(err))
  .then(() => pool.terminate());

เมื่อไรใช้ Workers vs child_process vs cluster

FeatureWorker Threadschild_processcluster
แชร์หน่วยความจำได้ (SharedArrayBuffer)ไม่ได้ไม่ได้
Overheadต่ำสูง (Process ใหม่)สูง (Process ใหม่)
Use CaseCPU-intensive ใน Node.jsรัน Script/Program ภายนอกScale HTTP Server หลาย Core
CommunicationpostMessage + SharedArrayBufferIPC (stdin/stdout/message)IPC (message)
Isolationแชร์ Process (Thread-level)แยก Process สมบูรณ์แยก Process สมบูรณ์
เมื่อไรใช้คำนวณหนัก ภายใน Node.jsรัน Python, FFmpeg, etc.Scale Web Server หลาย CPU

Use Cases จริง

1. Image Processing

// worker-image.js
const { parentPort, workerData } = require('worker_threads');
const sharp = require('sharp');

async function processImage() {
  const { inputPath, outputPath, width, height } = workerData;
  await sharp(inputPath)
    .resize(width, height)
    .jpeg({ quality: 80 })
    .toFile(outputPath);
  parentPort.postMessage({ status: 'done', outputPath });
}

processImage();

2. Data Parsing (CSV/JSON)

// แบ่ง File ขนาดใหญ่ให้แต่ละ Worker Parse
const { Worker } = require('worker_threads');

function parseChunk(chunk, workerId) {
  return new Promise((resolve, reject) => {
    const w = new Worker('./parse-worker.js', {
      workerData: { chunk, workerId }
    });
    w.on('message', resolve);
    w.on('error', reject);
  });
}

// Main: แบ่ง data เป็น chunks แล้วส่งแต่ละ Worker
const chunks = splitIntoChunks(bigData, 4);
const results = await Promise.all(
  chunks.map((chunk, i) => parseChunk(chunk, i))
);
const merged = results.flat();

3. Crypto Operations

// worker-hash.js
const { parentPort, workerData } = require('worker_threads');
const crypto = require('crypto');

const hash = crypto.pbkdf2Sync(
  workerData.password,
  workerData.salt,
  100000, // iterations
  64,
  'sha512'
);

parentPort.postMessage(hash.toString('hex'));

Worker Threads + Express

const express = require('express');
const Piscina = require('piscina');

const app = express();
const pool = new Piscina({
  filename: './heavy-computation.js',
  maxThreads: 4,
});

app.get('/compute/:n', async (req, res) => {
  try {
    const result = await pool.run({ n: parseInt(req.params.n) });
    res.json({ result });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Event Loop ยังตอบสนอง Request อื่น ๆ ได้ตามปกติ!
app.get('/health', (req, res) => {
  res.json({ status: 'ok' }); // ไม่ถูก Block
});

app.listen(3000);

Benchmarks: Single Thread vs Worker Threads

TaskSingle Thread4 WorkersSpeedup
Fibonacci(42) x 4~12 sec~3.5 sec3.4x
Image Resize 100 images~30 sec~8 sec3.75x
CSV Parse 500MB~20 sec~6 sec3.3x
PBKDF2 Hash x 100~15 sec~4 sec3.75x

Common Mistakes

  1. สร้าง Worker ใหม่ทุก Request: Overhead สูงมาก ใช้ Worker Pool แทน
  2. ส่งข้อมูลใหญ่ผ่าน postMessage: ใช้ SharedArrayBuffer หรือ transferList
  3. ใช้ Worker สำหรับ I/O Tasks: ไม่จำเป็น Node.js จัดการ I/O ได้ดีอยู่แล้ว
  4. ไม่จัดการ Error ใน Worker: Worker crash = ไม่มี response ต้อง handle error event
  5. สร้าง Worker มากเกินจำนวน CPU Core: ไม่ได้เร็วขึ้น อาจช้าลงด้วยซ้ำ
  6. ลืม Terminate Worker: Memory leak ถ้าไม่ terminate worker เมื่อเสร็จงาน

Alternatives: Bun Workers & Deno Workers

RuntimeAPIข้อดี
Node.jsworker_threads moduleMature, Ecosystem ใหญ่, SharedArrayBuffer
BunWeb Workers API (new Worker)เร็วกว่า Node.js 2-4x, API ง่ายกว่า
DenoWeb Workers APISecure by default, TypeScript built-in

สรุป

Worker Threads เป็นเครื่องมือสำคัญสำหรับ Node.js Developer ที่ต้องจัดการ CPU-intensive Tasks ช่วยให้ Event Loop ไม่ถูก Block ทำให้ Server ยังคงตอบสนองได้ดี ใช้ Worker Pool Library อย่าง piscina เพื่อจัดการ Workers อย่างมีประสิทธิภาพ และจำไว้ว่า Worker Threads เหมาะสำหรับ CPU-bound Tasks เท่านั้น ไม่ใช่ I/O-bound Tasks


Back to Blog | iCafe Forex | SiamLanCard | Siam2R