در Node.js یک محیط اجرا با مفسر V8 جاوااسکریپت در دسترس می باشد که به دلیل single-thread بودن اجازه اجرای چندین عملیات به صورت همزمان را نمیدهد. اگر برنامهی شما عملیات هایی را به صورت synchronous اجرا می کند ممکن هست برخی درخواست ها در حالت انتظار بماند تا اجرای عملیات های دیگر که ممکن هست مدت زمان بیشتری طول بکشد پایان یابد.
با این وجود Node.js یک برنامه multi-thread است. این موضوع زمانی قابل درک هست که از متدهای asynchronous کتابخانه های استاندارد آن استفاده میکنیم برای انجام عملیات هایی از قبیل خواندن یک فایل یا ایجاد یک درخواست شبکه. این عملیات ها به یک استخر جداگانه از thread ها تخصیص داده میشوند که Node از کتابخانه libuv زبان C برای ایجاد و نگهداری آنها استفاده میکند. با اینکه به نظر می رسد عملیات به صورت multi-thread انجام می شود، اما هنوز امکان دارد که عملیات های asynchronous سبب شود event-loop اصلی بلاک شود.
اما اگر بخواهید thread های مستقل را برای برنامه Node.js خود ایجاد کنید چگونه باید عمل کرد؟ multi-threading میتواند افزایش بهرهوری قابل توجهی را برای عملیات های محدود به CPU فراهم آورد و این قابلیت را ممکن می سازد تا عملیات های دلخواه به صورت همزمان انجام شوند. اگرچه Node.js یک multi-threading واقعی را ارائه نمیدهد، اما میتوان چیزی مشابه با آن را با استفاده از ماژول worker_threads ایجاد کنید. این مقاله توضیح میدهد که این ماژول چگونه عمل میکند و نحوه استفاده از آن در چندین مثال واقعی را نشان میدهد.
ماژول worker_threads شکلی از threading را پیاده سازی می کند که به شما امکان می دهد موازی سازی را به برنامه خود اضافه کنید. کد اجرا شده در یک worker thread در یک فرآیند فرزند(child) جداگانه اجرا می شود و از بلاک کردن event loop اصلی جلوگیری می کند.
در واقع worker thread ها thread های واقعی به معنای سنتی نیستند. آنها فرآیندهای مجزا هستند، به این معنی که آنها نمی توانند مستقیماً به محتوای محیط اجرای والد خود دسترسی داشته باشند. ارتباط بین worker thread ها و برنامه شما توسط یک سیستم پیام رسانی مبتنی بر رویداد تسهیل می شود.
اگرچه worker thread ها Node.js را به یک زبان multi-thread واقعی تبدیل نمی کنند. ولی در واقع یک مکانیسم مناسب برای اجرای همزمان چندین threads را اجرا می کنند و به شما امکان می دهد عملیات های سنگین را از event-loop اصلی جدا کنید.
می توان worker thread ها را در هر بخشی که شامل عملیات هایی است که استفاده زیادی از CPU می کنند استفاده کرد. آنها برای تسریع عملیات های I/O مناسب نیستند به دلیل سرباری که برای هر thread دارد. توابع کاربردی I/O داخلی Node.js برای انجام عملیات های مرتبط با فایل سیستمی و شبکه سریعتر و کارآمدتر خواهند بود.
به طور کلی هیچ محدودیتی از نظر استفاده از worker thread ها وجود ندارد، در ادامه چند مورد استفاده بیان می شود:
کاهش سرعت در تمام این عملیات ها به دلیل صرف زمان زیاد CPU برای اجرای کد است، برخلاف خواندن داده ها از دیسک یا شبکه. این عملیات ها کارهای تکراری هستند، بنابراین افزایش تعداد عملیات ها به صورت موازی بهترین راه حل برای بهبود عملکرد است. Worker-thread ها مکانیزمی برای دستیابی به این هدف است.
با وارد کردن ماژول worker_threads و اجرای new Worker() می توانید یک worker thread ایجاد کنید. به عبارتی دیگر یک فرآیند پردازش جدید برای اجرای یک فایل جاوا اسکریپتی که شما مشخص کرده اید ایجاد می شود. میتوانید پیغامهایی را با worker مبادله کنید تا در صورت وقوع رویدادها، آماده بودن دادهها برای پردازش یا بروز خطا، مطلع شوید.
کد نمونه:
const { Worker, isMainThread, parentPort workerData } = require("worker_threads"); if (isMainThread) { const worker = new Worker(__filename, {workerData: "Hello World"}); worker.on("error", err => console.error(error)); } else { const data = workerData; parentPort.postMessage(`You said \"${data}\".`); }
این کد ساده تمام اصول اولیه worker thread ها را نشان می دهد. کد می تواند به عنوان thread اصلی یا به عنوان یک worker-thread عمل کند. ماژول worker_threads فلگ isMainThread را در دسترس قرار می دهد که به شما امکان می دهد بررسی کنید که آیا کد توسط thread اصلی اجرا می شود یا خیر.
هنگامی که فایل توسط thread اصلی اجرا می شود - یعنی شما فایل را از خط فرمان خود راه اندازی کرده اید و اولین بلاک از دستور if اجرا می شود.
برای مطالعه بیشتر در مورد پارامترها می توانید به راهنمای استفاده از worker-thread در سایت nodejs.org مراجعه کنید.
برای بهتر درک کردن نحوه استفاده از worker-thread ها، میتوانید به نمونه کدی که در مسیر زیر قرار دارد، مراجعه کنید:
https://github.com/afshintalebi/nodejs-examples/tree/main/worket-thread
در انتها لازم به یادآوری هست که در NodeJS برنامه به صورت single-thread اجرا می شود و به همین دلیل بعضی مواقع برنامه ممکن است در حین اجرا بلاک شود تا پردازش قبلی بر روی thread اصلی پایان یابد. worker-thread با ایجاد محیطی مشابه کد مورد نظر شما را به صورت همزمان با thread اصلی اجرا می کند و این می تواند در بهبود اجرای برنامه شما بسیار موثر باشد.