~3 دقیقه مطالعه • بروزرسانی ۹ دی ۱۴۰۴
1. Introduction
The node:worker_threads module provides a way to run JavaScript in parallel threads. Each worker runs in its own isolated environment but can communicate efficiently with the main thread. This makes worker threads ideal for CPU-bound tasks such as image processing, compression, cryptography, or scientific computation.
2. Accessing the Module
const {
Worker,
isMainThread,
parentPort,
workerData,
threadId
} = require('node:worker_threads');
3. Why Use Worker Threads?
- Parallel CPU work: Offload heavy computations.
- Shared memory: Use
SharedArrayBufferfor fast data exchange. - Lower overhead: More lightweight than
child_processorcluster. - Zero-copy transfer: Transfer
ArrayBufferownership without cloning.
4. Basic Example
Offloading a heavy Fibonacci calculation to a worker:
// main.js
const { Worker, isMainThread } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename, { workerData: { num: 40 } });
worker.on('message', (result) => {
console.log('Fibonacci result:', result);
});
worker.on('exit', (code) => {
console.log('Worker exited with code', code);
});
} else {
const { workerData, parentPort } = require('node:worker_threads');
function fib(n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
parentPort.postMessage(fib(workerData.num));
}
5. Core Concepts
5.1 isMainThread
True in the main thread, false inside a worker.
5.2 workerData
Data passed from parent to worker using structured cloning.
5.3 parentPort
Communication channel from worker to parent.
5.4 threadId
Unique identifier for each worker thread.
6. Message Passing & Transfer Lists
6.1 Zero-Copy Transfer
const buffer = new ArrayBuffer(1024); parentPort.postMessage(buffer, [buffer]); // buffer is detached
6.2 SharedArrayBuffer
True shared memory between threads:
const shared = new SharedArrayBuffer(4); const view = new Int32Array(shared); Atomics.store(view, 0, 42);
7. MessageChannel
Create custom communication channels:
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
worker.postMessage({ port: port2 }, [port2]);
8. Advanced Features
8.1 Worker Pools
Reuse workers for repeated tasks to reduce overhead.
8.2 SHARE_ENV
new Worker(__filename, { env: worker_threads.SHARE_ENV });
8.3 Resource Limits
new Worker(__filename, {
resourceLimits: {
maxOldGenerationSizeMb: 200,
stackSizeMb: 4
}
});
8.4 BroadcastChannel
const bc = new BroadcastChannel('updates');
bc.postMessage('Hello workers!');
8.5 Locks API (Experimental)
await locks.request('critical', async () => {
// Only one worker enters here at a time
});
8.6 Profiling
await worker.startCpuProfile(); await worker.getHeapSnapshot();
8.7 Termination
await worker.terminate();
9. Best Practices
- Use workers only for CPU-heavy tasks.
- Use transfer lists for large buffers.
- Use SharedArrayBuffer carefully to avoid race conditions.
- Use worker pools for repeated workloads.
- Avoid blocking the main thread.
10. When Not to Use Worker Threads
- Simple asynchronous I/O.
- Very short tasks (overhead > benefit).
- Tasks that cannot be parallelized.
Conclusion
The node:worker_threads module brings true parallelism to Node.js. With shared memory, zero-copy transfers, worker pools, and advanced synchronization tools, it is ideal for CPU-bound workloads such as image processing, cryptography, compression, and scientific computing. Worker threads significantly enhance performance while keeping the main event loop responsive.
نوشته و پژوهش شده توسط دکتر شاهین صیامی