همه چیز راجب thread-pool ها 🔥
thread-pool ها مجموعه ای از رشته های کارگر (worker thread) هستن. اونها توسط یک زمانبندی مرکزی مديريت و وظایف (task) رو به pool ارسال میکنن و زمانبندی، اون هارو بین رشته های موجود برای اجرا توزیع میکنه. این مکانیزم امکان برنامه ریزی کارآمد و استفاده از منابع رو فراهم میکنه.
مزایای استفاده از thread-pool
کاهش overhead: ساخت و از بین بردن رشته ها میتونه پرهزینه باشه. thread-pool با استفاده مجدد از رشته ها، باعث میشه overhead کاهش پیدا کنه.
بهبود عملکرد: thread-pool ها میتونن با توزیع وظایف بین چندین رشته، عملکرد برنامه رو بهبود ببخشند.
سادگی استفاده: thread-pool با استفاده از concurrency، برنامه ها رو ساده تر میکنه.
برای درک بهتر به مثال زیر توجه کنید
const crypto = require('crypto')
const start = Date.now()
crypto.pbkdf2('a','b',10000,512,'sha512',()=>{
console.log('1:',Date.now() - start);
})
crypto.pbkdf2('a','b',10000,512,'sha512',()=>{
console.log('2:',Date.now()- start);
})
crypto.pbkdf2('a','b',10000,512,'sha512',()=>{
console.log('3:',Date.now()- start);
})
crypto.pbkdf2('a','b',10000,512,'sha512',()=>{
console.log('4:',Date.now()- start);
})
crypto.pbkdf2('a','b',10000,512,'sha512',()=>{
console.log('5:',Date.now()- start);
})
تابع PBKDF2 اکثرا برای رمزنگاری رشته ها استفاده میشه.
اگه بخوایم از thread-pool استفاده نکنیم، هربار که این تابع رو فراخوانی میکنیم یک رشته جدید ساخته میشه، که منجر به overhead بالا و عملکرد ضعیف میشه. اما با استفاده از thread-pool رشته ها میتونن مجددا استفاده بشن و وظایف بین اونها توزیع میشه. این امر منجر به overhead پایینتر و عملکرد بهتر میشه.
شاید درکش در نگاه اول سخت باشه، برای همین یک آشپزخانه در رستوران شلوغ رو تصور کنید. آشپزها (threads) برای تهیه ظروف (tasks) کار میکنن، با این حال، مدیریت چندین سفارش به طور همزمان میتونه طاقت فرسا باشه، و اینجاست که یک thread-pool وارد عمل میشه.
یک thread-pool به عنوان یک تیم سازمان یافته از آشپزها عمل میکنه، که توسط آشپز (scheduler) مدیریت میشه. هنگامی که یک مشتری سفارشی رو ارسال میکنه (task is submitted)، گارسون اون رو به آشپز موجود (thread) اختصاص میده و اگر همه آشپزها مشغول باشن، سفارش تا زمانی که یک آشپز در دسترس باشه در صف هست.
این توزیع کارآمد تضمین می کنه که هیچ آشپزی بیکار نیست، و سفارشات به سرعت پردازش میشن، به طور مشابه، در U-V Alive، thread-pool تماسهای متعدد به تابع PBKDF2 رو مدیریت میکنه، و مانع از تحت فشار قرار گرفتن CPU توسط وظایف محاسباتی فشرده میشه.
استخر رشته ها(thread-pool) در پروژه های واقعی چجوری استفاده میشه ؟
به عنوان مثال، یک وب سایت e-commerce، ممکنه از یک thread-pool برای رسیدگی به چندین درخواست مشتری به طور همزمان استفاده کنه، با ورود سفارشات thread-pool اون ها رو بین رشته های موجود توزیع می کنه، و اطمینان حاصل می کنه که درخواست هر مشتری به سرعت و بدون بارگذاری بیش از حد سرور پردازش بشه.
نکاتی که باید بهش توجه داشته باشیم:
- تعداد دیفالت thread ها در thread-pool دو عدد هست.
- افزایش تعداد thread ها در thread-pool میتونه عملکرد رو برای کارهای فشرده محاسباتی بهبود ببخشه.
- عملکرد واقعی thread-pool بسته به پیکربندی سخت افزار و نرم افزار خاص، ممکنه متفاوت باشه.
به سوالات رایج در مورد thread-pool ها میرسیم 🤔
1. آیا میتونیم از thread-pool در قسمت هایی به جز توابع Node.js استفاده کنیم؟
بله ما میتونیم کد جاوا اسکریپت سفارشی بنویسیم، که از thread-pool استفاده کنه. به صورت کلی thread-pool ها منحصر به توابع Node.js نیستن. آنها مکانیزمی رو برای مدیریت اجرای هر کار جاوا اسکریپتی، ارائه میدن که میتونن از پردازش موازی، صرف نظر از منشأ آن بهره مند بشن.
2.چه توابعی در کتابخانه استاندارد Node.js از thread-pool استفاده می کنن؟
مشخص کردن دقیق تمام توابعی که در کتابخانه استاندارد Node.js، ازthread-pool استفاده میکنند دشواره اما به طور کلی، میشه گفت که دو دسته از توابع از این قابلیت بهره میبرد:
1. توابع I/O ناهمزمان:
- توابع مربوط به ماژول
fsمانندreadFile(),writeFile(), وopen(). - توابع مربوط به ماژول
dnsمانندlookup()وresolve(). - توابع مربوط به ماژول
netمانندconnect(),write(), وread().
این توابع برای انجام عملیات I/O به صورت غیرمسدود کننده طراحی شدن، به این معنی که بدون منتظر موندن برای تکمیل شدن عملیات، به کد شما باز میگردن. برای دستیابی به این امر، اغلب از thread-pool برای واگذاری وظایف I/O به threadهای مجزا استفاده میکنن.
2. توابع محاسباتی سنگین:
برخی از توابع نیز در ماژول crypto مانند pbkdf2(), randomBytes(), و randomFill() از thread-pool استفاده میکنن.
این توابع میتونن محاسبات فشرده ای رو انجام که قابلیت اینو دارن، thread اصلی Node.js رو مسدود کنن. برای جلوگیری از این امر، ممکنه از thread-pool برای اجرای این محاسبات در thread های جداگانه استفاده کنن.
- ⚠️لیست توابع بالا جامع نیست و ممکنه توابع دیگری در کتابخانه استاندارد Node.js وجود داشته باشه، که از thread-pool به روشی خاص استفاده کنن، جزئیات پیاده سازی و رفتار دقیق میتونه بسته به سیستم عامل و نسخه Node.js متفاوت باشه.
3. این موضوع thread-pool چگونه در حلقه رویداد قرار می گیره؟
درواقع thread-pool از طریق یک آرایه به نام "pendingOperations" با حلقه رویداد تعامل داره. هنگامی که یک تسک به مجموعه موضوعات ارسال میشه، به این آرایه هم اضافه میشود. ایونت لوپ به صورت دوره ای این آرایه رو برای کارهای معلق بررسی میکنه و اون رو مطابق با آن اجرا می کنه.
کد پایین یک شبیه سازی از نحوه کاره thread-pool در ایونت لوپ هست.
const pendingTimers = [];
const pendingOsTasks = [];
const pendingOperations = [];
// New timers, tasks , operations are recorded from mFile running
myfile.runContents();
function shouldContinue() {
// check one: any pending setTimeout, setInterval
// check two: any pending os task ? ( Like server listening to port)
// check three: any pending long running operations? (like fs module)
return (
pendingTimers.length || pendingOperations.length || pendingOsTasks.length
);
}
while(shouldContinue()){
// 1) Node looks at pendingTimers and sees if any functions
// are ready to be called
// 2) node Looks at pendingOSTasks and pendingOperations and calls relevant callbacks
// 3) Pause execution. Continue When ...
// - a new pendingOSTask is done
// - a new pendingOperation is done
// - a timer is about to complete
// 4) look at pendingTimers. Call any setImmediate
// 5) handle any 'close' events
}
این کد یک تصویر ساده از نحوه عملکرد ایونت لوپ در کنار thread-pool ارائه میده که جزئیات به صورت زیر هست:
pendingTimers : این آرایه اطلاعاتی در مورد تایمرهای تنظیم شده با استفاده از "setTimeout" یا "setInterval" در خودش داره.
pendingOsTasks : این آرایه وظایف مربوط به سیستم عامل مانند اتصالات شبکه یا عملیات I/O فایل را ردیابی می کند.
pendingOperations : این آرایه عنصر کلیدی برای thread-pool هست. ارجاع به وظایف ارسال شده به Thread Pool رو که در حال اجرا هستن یا در حال اجرا هستن ذخیره می کنه.
تابع 'shouldContinue' این سه آرایه را بررسی می کنه تا ببینه آیا تسکی در حال تعلیق هست یا خیر، که اگر وجود داشته باشه، به این معنیه که ایونت لوپ باید به کار خود ادامه بده. اجرای وظایف در مجموعه thread ها در ایونت لوپ، بر اساس این پیاده سازی خاص رخ میده.
به صورت کلی thread-pool یک ابزار ضروری برای هر برنامه نویسه. با درک اصول و پیاده سازی اون ها، دولوپرها میتونن برنامه های کاربردی مقیاس پذیر بسازن، که نیاز محیط های محاسباتی رو باهاش برطرف کنن.
#nodejs #thread #thread_pool
@CodeModule