~3 دقیقه مطالعه • بروزرسانی ۹ دی ۱۴۰۴
1. معرفی
ماژول node:worker_threads امکان اجرای جاوااسکریپت در ریسههای جداگانه را فراهم میکند. هر Worker محیط مستقل خود را دارد اما میتواند با ریسهٔ اصلی ارتباط سریع و کارآمد برقرار کند. این قابلیت برای کارهای CPU-محور بسیار مناسب است.
2. نحوهٔ دسترسی
const {
Worker,
isMainThread,
parentPort,
workerData,
threadId
} = require('node:worker_threads');
3. چرا از Worker Threads استفاده کنیم؟
- اجرای موازی کارهای سنگین CPU مانند پردازش تصویر، رمزنگاری، فشردهسازی.
- حافظهٔ اشتراکی با SharedArrayBuffer برای تبادل سریع داده.
- هزینهٔ کمتر نسبت به child_process یا cluster.
- انتقال بدون کپی برای ArrayBuffer و MessagePort.
4. مثال پایه
محاسبهٔ سنگین Fibonacci در یک 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. مفاهیم اصلی
5.1 isMainThread
در ریسهٔ اصلی true و داخل Worker مقدار false دارد.
5.2 workerData
دادهای که هنگام ساخت Worker ارسال میشود.
5.3 parentPort
کانال پیام Worker → Parent.
5.4 threadId
شناسهٔ یکتا برای هر Worker.
6. پیامرسانی و Transfer List
6.1 انتقال بدون کپی
const buffer = new ArrayBuffer(1024); parentPort.postMessage(buffer, [buffer]); // buffer از دسترس خارج میشود
6.2 SharedArrayBuffer
حافظهٔ واقعاً مشترک بین ریسهها:
const shared = new SharedArrayBuffer(4); const view = new Int32Array(shared); Atomics.store(view, 0, 42);
7. MessageChannel
ایجاد کانال پیام اختصاصی:
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
worker.postMessage({ port: port2 }, [port2]);
8. قابلیتهای پیشرفته
8.1 Worker Pool
استفادهٔ مجدد از Workerها برای کاهش هزینهٔ ساخت.
8.2 SHARE_ENV
new Worker(__filename, { env: worker_threads.SHARE_ENV });
8.3 محدودیت منابع
new Worker(__filename, {
resourceLimits: {
maxOldGenerationSizeMb: 200,
stackSizeMb: 4
}
});
8.4 BroadcastChannel
const bc = new BroadcastChannel('updates');
bc.postMessage('Hello workers!');
8.5 Locks API (آزمایشی)
await locks.request('critical', async () => {
// فقط یک Worker در لحظه وارد میشود
});
8.6 پروفایل CPU و Heap Snapshot
await worker.startCpuProfile(); await worker.getHeapSnapshot();
8.7 خاتمهٔ Worker
await worker.terminate();
9. بهترین شیوهها
- فقط برای کارهای CPU-محور استفاده شود.
- برای دادههای بزرگ از Transfer List استفاده کنید.
- SharedArrayBuffer را با احتیاط و آگاهی از Race Condition بهکار ببرید.
- برای کارهای تکراری از Worker Pool استفاده کنید.
- از مسدود کردن ریسهٔ اصلی پرهیز کنید.
10. مواردی که نباید از Worker Threads استفاده کرد
- کارهای سادهٔ I/O.
- وظایف بسیار کوتاه (هزینهٔ Worker بیشتر از سود آن است).
- کارهایی که قابل موازیسازی نیستند.
نتیجهگیری
ماژول node:worker_threads امکان اجرای موازی واقعی را در Node.js فراهم میکند. با پشتیبانی از حافظهٔ اشتراکی، انتقال بدون کپی، Worker Pool و ابزارهای همگامسازی، این ماژول برای پردازشهای سنگین مانند رمزنگاری، پردازش تصویر، فشردهسازی و محاسبات علمی کاملاً مناسب است. استفادهٔ صحیح از Worker Threads میتواند کارایی برنامه را بهطور چشمگیری افزایش دهد بدون اینکه حلقهٔ رویداد مسدود شود.
نوشته و پژوهش شده توسط دکتر شاهین صیامی