Thread چیست؟ — به زبان ساده و جامع
Thread (نخ | رشته) توالی محدودی از دستورالعملهای برنامه نویسی به حساب میآید. Thread جریانی واحد از کنترل در یک برنامه است که امکان اجرای چند وظیفه (Task) را در درون یک «پردازه» یا پردازش به وجود میآورد. این تعاریف کوتاه احتمالاً پیچیده و ناقص هستند. بنابراین، در این نوشته ابتدا در سطحی جامع به این سوال پاسخ داده شده است که Thread چیست و سپس برای درک بهتر و کاملتر معنی Thread ، سایر مفاهیم مرتبط با آن نیز شرح و به سوالات رایج پیرامون مفهوم Thread پاسخ داده شده است. پس از شرح مباحث نظری و تعاریف، نمونه کدهای ایجاد Thread در زبانهای جاوا، C++، سی شارپ و پایتون نیز ارائه شدهاند و همچنین مفاهیم مهم در برنامه نویسی Thread شرح داده شده است.
در همین ابتدا به سوال اصلی این مقاله یعنی «Thread چیست» پاسخ داده شده است.
Thread چیست ؟
Thread کوچکترین واحد اجرایی در سیستم عامل است. Thread واحد اساسی اجرایی یا واحد اساسی در بهرهبرداری از CPU به حساب میآید. Thread یکی از مفاهیم مهم در سیستمهای عامل محسوب میشود. Thread موجودیتی در درون یک پردازه (پروسه | Process) است که میتواند برای اجرا زمانبندی شود. یک برنامه تحت اجرا به عنوان یک Process شناخته میشود.
هر برنامه ممکن است تعدادی پردازه مرتبط داشته باشد و هر پردازه میتواند دارای چند Thread باشد که این Threadها آن پردازه را اجرا میکنند. تمام Threadهای یک پردازه فضای آدرس مجازی و منابع سیستمی آن پردازه را با یکدیگر شریک میشوند.
کارکرد Thread به چه صورت است؟
Threadها معمولاً به وسیله یک زمانبند (Scheduler) مدیریت میشوند. یک زمانبند بخش استانداردی از یک سیستم عامل است. برای ایجاد یک Thread ابتدا باید یک Process یا پردازه ایجاد شود. پس از آن، پردازه یک Thread ایجاد میکند و سپس این Thread اجرا خواهد شد. این فرآیند میتواند برای بازه زمانی کوتاه یا طولانی بسته به نوع پردازه رخ بدهد. فارق از اینکه چه مدت زمان مورد نیاز باشد، این منظر به وجود میآید که کامپیوتر دارد کارهای زیادی را در یک لحظه انجام میدهد.
هر پردازه حداقل دارای یک Thread است، اما سقفی برای تعداد Threadهای یک پردازه وجود ندارد و پردازه میتواند هر تعداد Thread مورد نیازش را به کار گیرد. برای وظایف تخصصی، هر چه تعداد Thread بیشتری وجود داشته باشد، عملکرد و کارایی سیستم بهتر خواهد بود. در ادامه پاسخ به سوال «Thread چیست» در این بخش، اجزای تشکیل دهنده Thread شرح داده شدهاند.
اجزای تشکیل دهنده Thread کدامند؟
Thread از شناسه Thread ، شمارنده برنامه، مجموعه ثبات (Register) و پشته (Stack) تشکیل شده است. علاوه بر این، یک Thread بخش کدها، بخش دادهها و سایر منابع سیستم عامل نظیر فایلها و سیگنالهای باز را با سایر Threadهای همان پردازه به اشتراک میگذارد.
به بیان دقیقتر، هر Thread کنترل کنندههای استثنا، اولویت زمانبندی، ذخیرهگاه محلی Thread، یک شناسه منحصربهفرد Thread و مجموعهای از ساختارها را نگهداری میکند. سیستم از این ساختارها برای ذخیره مفاد Thread یا Thread Context تا هنگام زمانبندی استفاده میکند. همچنین، Threadها میتوانند مفاد امنیتی خودشان را داشته باشند.
اجزای Thread Context شامل چه مواردی است؟
مفاد Thread یا Thread Context شامل مجموعه ثباتهای ماشین، پشته کرنل، یک بلوک محیطی Thread و یک پشته کاربر در فضای آدرس پردازه Thread است. با توجه به اینکه مفهوم Thread در قالب یک Process یا پردازه نمود پیدا میکند، برای درک بهتر پاسخ سوال «Thread چیست» بهتر است در ادامه شرحی از مفهوم پردازه ارائه شود.
Process چیست؟
در علوم محاسبات و کامپیوتر، یک پردازه (پردازش | Process) وهلهای (نمونه | Instance) از یک برنامه کامپیوتری به حساب میآید که به وسیله یک یا تعداد زیادی از Threadها در حال اجرا است.
پردازه شامل کدهای برنامه و رفتار آن است. در حالی که یک برنامه مجموعهای از دستورالعملهای منفعل ذخیره شده روی دیسک است، اجرای آن دستورالعملها پس از بارگذاری از روی دیسک و انتقال آن به حافظه رم (RAM) یک پردازه محسوب میشود. مفهوم پردازه و ارتباط آن با Thread و اجزایی نظیر قطعات سختافزاری (دیسک، RAM و CPU)، دستورالعملها، زمانبند و سایر موارد به خوبی در تصویر زیر نمایش داده شدهاند.
اجزای Process چه هستند؟
در این بخش از مقاله «Thread چیست» فهرستی از اجزای سازنده یک Process یا پردازه بدین صورت فهرست شده است:
- یک فضای آدرس مجازی
- کدهای اجرایی
- دسترسی باز به اشیا سیستم
- بافت امنیتی
- یک شناسه پردازه منحصربهفرد
- متغیرهای محیطی
- کلاس اولویت
- اندازههای مجموعه کاری کمینه و بیشینه
- حداقل یک Thread اجرایی
Context Switching یکی از مفاهیم در سیستمهای عامل محسوب میشود که در طول مقاله «Thread چیست» چند بار به آن اشاره شده است. بنابراین، در ادامه شرح مختصری از مفهوم Context Switching ارائه شده است.
Context Switching چیست؟
«Context Switching» یا «تغییر زمینه» شامل ذخیره مفاد یا وضعیت یک Process در سیستم عامل است تا بتوان در زمان لازم آن را دوباره بارگذاری کرد و اجرای آن پردازه از همان نقطه توقف از سر گرفته شود. Context Switching ویژگی یک سیستم عامل چندوظیفهای است و امکان استفاده چند پردازه از یک CPU واحد را فراهم میکند. در طول این مقاله چند بار از اصطلاح Context Switching استفاده شده است. Context Switching به فرایند ذخیره وضعیت سیستم برای یک وظیفه اشاره دارد تا امکان توقف موقت انجام آم وظیفه و ادامه انجام وظیفه دیگر فراهم شود.
همچنین، یک تغییر زمینه میتواند در پی وقوع یک وقفه (Interrupt) نیز رخ دهد، مانند وقتی که یک وظیفه نیاز داشته باشد به حافظه دیسک دسترسی یابد. در چنین موقعیتی میتوان زمان CPU را برای سایر وظیفهها آزادسازی کرد. همچنین برخی از سیستمهای عامل برای تغییر موقعیت از وظایف حالت کاربر به وظایف حالت کرنل به Context Switch نیاز دارند. فرایند تغییر زمینه میتواند تاثیر منفی در عملکرد سیستم به همراه داشته باشد. بنابراین، در این بخش از نوشته «Thread چیست» شرحی از مفهوم Context Switching ارائه شد تا آشنایی با این مفهوم برای مطالعه ادامه متن وجود داشته باشد. حال در ادامه به این سوال پاسخ داده شده است که دو مفهوم Process و Thread چه تفاوتهایی با یکدیگر دارند.
تفاوت Process و Thread چیست؟
به طور خلاصه، Process یا پردازه مجموعهای از کدها، حافظه، داده و سایر منابع است. اما، Thread توالی از کدها به حساب میآید که در داخل محدوده یک پردازه اجرا میشود. با تعریف هر یک از مفاهیم پردازه و Thread تفاوت Process و Thread تا حد زیادی قابل درک است.
اما در این بخش از مقاله «Thread چیست» با جزئیات بیشتری به بررسی تفاوتهای این دو مفهوم پرداخته شده است. میتوان هم پردازه و هم Thread را توالیهای اجرایی مستقل تلقی کرد.
تفاوت بارز Process و Thread این است که Threadها (همه از یک پردازه) در یک فضای حافظه اشتراکی اجرا میشوند، در حالی که پردازهها در فضاهای حافظه جداگانه عمل میکنند. Threadها بیش از اینکه قابلیت CPU محسوب شوند، یک قابلیت محیطهای عملیاتی (سیستم عامل) به حساب میآیند. هر پردازه منابع مورد نیاز برای اجرای یک برنامه را فراهم میکند. حال در انتهای این بخش، فهرستی از تفاوتهای کلیدی Process و Thread ارائه شده است:
- Process به معنای یک برنامه در حال اجراست، در حالی که Thread به معنی بخشی (Segment) از یک Process به شمار میرود.
- یک Process سبکوزن نیست، در حالی که Thread کم حجم و سبکوزن است.
- زمان بیشتری برای بستن یک Process نسبت به یک Thread نیاز است.
- ایجاد یک Process نیز نسبت به ایجاد یک Thread نیازمند زمان بیشتری است.
- برای تغییر زمینه (Context Switching) در Process نسبت به Thread نیاز به زمان بیشتری وجود دارد.
- یک process به میزان زیادی منزوی است، در حالی که Threadها حافظه و منابع دیگری را در داخل یک پردازه با هم به اشتراک میگذارند.
Thread در سی پی یو چیست ؟
Threadها قطعاتی مجازی از کدها هستند که هسته فیزیکی یک CPU را به چندین هسته مجازی تقسیم میکنند. یک هسته CPU میتواند تا دو Thread در هر هسته داشته باشد. برای مثال، در صورتی که یک سیپییو دو هستهای (Dual Core) باشد، دارای چهار Thread خواهد بود. همچنین، اگر یک CPU هشت هستهای (Octal Core) باشد، دارای شانزده Thread خواهد بود. Thread به وسیله یک پردازه ایجاد میشود.
هر بار که کاربر برنامهای را باز میکند، یک Thread ایجاد میشود که مسئولیت مدیریت تمام وظایف آن اپلیکیشن خاص را بر عهده خواهد داشت. به همین شکل، هرچه اپلیکیشن بیشتری باز شود، Threadهای بیشتری ایجاد خواهند شد. برای درک بهتر مفهوم Thread در CPU ، بهتر است شرح مختصر و سادهای از چیستی CPU ارائه شود. این کار در ادامه این بخش از مطلب «Thread چیست» انجام شده است.
CPU چیست ؟
CPU سرنامی برای Central Processing Unit به معنی واحد پردازش مرکزی است. CPU بخش حیاتی هر کامپیوتر است که نحوه عملکرد سیستم و چگونگی انجام عملیات را تعیین میکند. CPU دستورالعملهای اساسی کاربر را دریافت میکند و کارهای لازم را به سایر قطعات سیستم ارجاع میدهد. با هدایت وظایف پیچیده به قطعاتی که به بهترین نحو میتوانند آنها را اجرا کنند، از حداکثر توان عملیاتی سیستم استفاده میشود.
CPU چه کاری انجام میدهد؟
CPU دادهها را از یک برنامه خاص دریافت میکند، توالی از محاسبات را روی آنها انجام میدهد و دستورات را به اجرا میگذارد. CPU یک چرخه سه بخشی را اجرا میکند که چرخهای تکراری از دریافت (Fetch)، رمزگشایی (Decode) و اجرا (Execute) است.
- در فاز اول، CPU دستورالعملها را از حافظه سیستم دریافت میکند.
- در فاز دوم، CPU دستورالعملهای دریافتی را رمزگشایی میکند.
- در آخرین مرحله، اطلاعات رمزگشایی شده از CPU عبور میکنند تا به واحدی برسند که باید وظیفه مربوطه را اجرا کند.
چرخه فوق بارها و بارها برای هر عملیات و دستور صادر شده از سمت کاربر تکرار میشود. CPU بخشی حیاتی در هر سیستم به حساب میآید و به طور هماهنگ با Threadها کار میکند. حال در ادامه مقاله «Thread چیست» به شرح نحوه ایجاد و تخصیص Thread در CPU پرداخته شده است.
نحوه ایجاد و تخصیص Thread در CPU چگونه است؟
Threadها همیشه به وسیله سیستم عامل برای اجرای یک وظیفه در یک برنامه خاص ایجاد می شوند. ابتدا یک Thread وجود دارد که مربوط به کدهای انجام محاسبات یک هسته CPU است و به آن Thread اصلی (Primary Thread) گفته میشود.
این Thread اطلاعات را از کاربر دریافت میکند و سپس Thread دیگری ایجاد میکند و کاری که باید انجام شود را به آن Thread جدید اختصاص میدهد. به همین شکل، در صورت دریافت یک دستورالعمل دیگر، Thread دیگری ایجاد میشود و آن وظیفه به Thread جدید اختصاص داده خواهد شد.
مثالی برای درک بهتر عملکرد Thread در CPU
در این بخش از مقاله «Thread چیست» مثالی برای درک بهتر نحوه عملکرد و چیستی Thread در CPU و سیستم عامل ارائه شده است. میتوان یک اپلیکیشن گوشی هوشمند را در نظر گرفت. وقتی کاربر برنامهای را در گوشی باز میکند، دایرهای چرخان روی صفحه نمایش داده میشود. این دایره چرخان پردازهای است که توسط یک Thread تنها برای این مقصود ایجاد شده است.
سپس، Thread دیگری اطلاعات را بارگذاری میکند و آن را در رابط کاربری گرافیکی نمایش میدهد. تنها مسئلهای که ایجاد Threadها را محدود میکند، تعداد Threadهایی خواهد بود که یک CPU میتواند فراهم کند. تعداد Threadهای قابل ارائه در هر CPU با سیپییو دیگر متفاوت است. نمایشی انتزاعی از آنچه در هنگام باز شدن یک اپلیکیشن موبایل رخ میدهد و نحوه کارکرد Threadها در این مثال به صورت زیر است:
همانطور که در تصویر فوق ملاحظه میشود، با باز کردن یک اپلیکیشن توسط کاربر، Thread اصلی مربوط به یکی از هستههای CPU، یک Thread جدید را برای نمایش دایره چرخان روی صفحه ایجاد میکند و این وظیفه را به آن Thread اختصاص میدهد. به طور همزمان، Thread دیگری برای بارگذاری و نمایش اطلاعات در رابط کاربری (UI) ایجاد شده و شروع به اجرای وظیفه خود میکند. یکی دیگر از مفاهیمی که درباره Thread در CPU مطرح میشود، «Hyper Threading» است که در ادامه این بخش از مقاله «Thread چیست» به معرفی آن پرداخته شده است.
Hyper Threading چیست؟
فناوری Hyper Threading به یک هسته CPU واحد اجازه میدهد به عنوان دو هسته عمل کند که منجر به افزایش سرعت اجرای یک برنامه یا اپلیکیشن میشود. حتی با یک هسته CPU میتوان به گونهای شبیهسازی انجام داد که گویی دو هسته وجود دارد. هر چه تعداد Threadها بیشتر باشد، میزان عملکرد و کارایی سیستم بهتر خواهد بود. در صورتی که CPU دو هستهای باشد، با استفاده از Hyper Threading این دو هسته مانند چهار هسته عمل خواهند کرد.
Hyper Threading اولین تلاش شرکت اینتل برای تجهیز کامپیوترهای شخصی کاربران نهایی به محاسبات موازی محسوب میشود. از این روش اولین بار در پردازندههای دسکتاپ Pentium 4 در سال 1381 شمسی (۲۰۰۲ میلادی) استفاده شد. پردازندههای Pentium 4 در آن زمان تنها دارای یک هسته CPU بودند. بنابراین، این CPU تنها یک وظیفه در لحظه انجام میدهد و امکان اجرای چند عملیات همزمان در آن وجود ندارد. یک CPU تکهستهای با داشتن قابلیت Hyper Threading برای یک سیستم عامل به عنوان دو CPU منطقی در نظر گرفته میشود. در ادامه بخش Thread در CPU از نوشته «Thread چیست» به چگونگی همکاری Threadها و CPUها پرداخته شده است.
Threadها و CPUها چگونه باهم کار میکنند؟
دانستن نحوه کارکرد Threadها و CPUها با یکدیگر به درک بهتر پاسخ سوال «Thread چیست» کمک میکند. برای سادهسازی ایده نهفته در Threadها از واژه نخ یا همان Thread برای این مفهوم استفاده میشود. اما در واقع باید به این مفهوم به عنوان «رشتهای از عملیات» (Thread of Execution) نگریست. وقتی دستوری اجرا میشود، CPU فرآیند دریافت، کدگشایی و اجرای عملیات را آغاز میکند تا آن دستور را انجام دهد. CPUها جریان دستورالعملهایی را اجرا میکنند که با اجرای یک دستور توسط کاربر نوبت به اجرایشان فرا میرسد. بنابراین، CPUها و Threadها برای اجرای قابلیتهای مورد نیاز کاربر با یکدیگر همکاری میکنند.
هر بار که پردازنده Thread جدیدی را بارگذاری میکند، Thread ابتدا در حافظه اصلی ذخیره میشود. پس از حذف دستورالعملهای Thread اولیه از چرخه، یک Thread جدید میتواند ایجاد شود. Thread جدید هم کار سهمرحلهای دریافت، کدگشایی و فرایند اجرا را آغاز میکند و این چرخه به همین شکل ادامه پیدا خواهد کرد. در ادامه مقاله «Thread چیست» به آموزش مفهوم مهم Multithreading در بحث Thread چیست پرداخته شده است.
Multi Thread یا چندنخی چیست؟
چندنخی یا Multithreading قابلیت واحد پردازنده مرکزی یا یک هسته تکی در پردازنده چند هستهای برای اجرای چند Thread به صورت همزمان است که به وسیله سیستم عامل پشتیبانی میشود.
امروزه یک پروسه میتواند شامل چندین Thread مختلف باشد. یک پردازه سنتی (قدیمی) که به آن پردازه سنگین وزن هم گفته میشود، تنها دارای یک Thread کنترلی است.
اما، در صورتی که یک پروسه دارای چندین Thread کنترلی باشد، میتواند بیش از یک وظیفه را در لحظه انجام دهد. بنابراین به استفاده همزمان از چند Thread در یک پردازه، Multithreading یا چندنخی گفته میشود. به همین دلیل، Threadها بسیار کاربردی هستند و استفاده از آنها بهرهوری سیستم را به میزان زیادی افزایش میدهد. حال در ادامه شرح Multithreading به آموزش تفاوتهای یک پروسه چند نخی با پروسه تکنخی پرداخته شده است.
تفاوت پردازه Multi Thread با Single Thread در چیست؟
میتوان با استفاده از دو نمودار زیر درک بهتری نسبت به Threadها و تفاوت پردازه Multi Thread با Single Thread به دست آورد:
در سمت چپ تصویر فوق، نمودار یک پردازه تک نخی یا Single Thread نشان داده شده است. در واقع تمام بلوک سمت چپ به عنوان یک پردازه در نظر گرفته میشود. این پردازه تنها دارای یک Thread است. همانطور که در تصویر مشخص شده، هر پردازه از فایلها، دادهها، کدها، ثباتها و یک پشته تشکیل شده است. در بلوک سمت راست نیز نموداری از یک پردازه چندنخی رسم شده است.
در نمودار پردازه چندنخی فوق، سه Thread رسم شده است. هر Thread دارای پشته و ثباتهای مربوط به خودش است. همچنین، در نمودار سمت راست ملاحظه میشود که فایلها، دادهها و کدهای پردازه چندنخی بین این سه Thread تقسیم شده است. به این ترتیب یک پردازه چندنخی میتواند چندین وظیفه و کار را در لحظه انجام دهد. زیرا هر Thread وظیفه متفاوتی را بر عهده خواهد داشت. حال در ادامه این بخش از مقاله «Thread چیست» به شرح نحوه مشاهده Threadهای پروسههای ویندوز و ارائه مثالی از چندنخی از این طریق پرداخته شده است.
مشاهده Threadهای یک پردازه در ویندوز
به عنوان مثال، میتوان پردازههای سیستم عامل ویندوز را در برنامه مدیریت وظایف (Task Manager) و سربرگ Processes ملاحظه کرد. برای دیدن Threadهای هر پردازه میتوان از برنامه «ProcessThreadsView» استفاده کرد. این برنامه به صورت رایگان از طریق سایت آن قابل دانلود و نصب است.
برای مشاهده Threadهای این برنامه ابتدا باید یکی از پردازههای فهرست شده را انتخاب کرد. با این کار، جزئیات هر Thread از پردازه مربوطه در برنامه ProcessThreadsView در قالب جدولی به صورت تصویر زیر نمایش داده میشود:
مثالی از یک پردازه چندنخی
برای مثال، در تصویر فوق تعدادی از Threadهای مربوط به یکی از پردازههای مرورگر گوگل کروم فهرست شدهاند. مطابق تصویر فوق، اولین ستون مربوط به شناسه Thread است که پیشتر به عنوان یکی از مشخصات و اجزای تشکیل دهنده Thread معرفی شد. ستونهای متعددی حاوی اطلاعات مختلف پیرامون هر Thread وجود دارد. در ستونهای انتهایی، آدرس Thread و جزئیاتی پیرامون پشته مربوط به هر Thread ملاحظه میشود. بنابراین، پردازههای مرورگر گوگل کروم چندنخی هستند و این مرورگر میتواند در لحظه چندین وظیفه را انجام دهد.
مثلاً یکی از Threadهای پردازهای از کروم برای نمایش یک صفحه وب در پنجره مرورگر استفاده میشود و Thread دیگری برای دانلود کردن یک فایل از اینترنت مورد استفاده قرار میگیرد. به همین دلیل، کاربر کروم میتواند حین دانلود فایل، به طور همزمان صفحات وب را هم باز کند. این مزیت پردازههای چندنخی است. اما در صورتی که پردازهها تکنخی باشند، برای مثال در حین دانلود یک فایل نمیتوان کار دیگری مثل باز کردن صفحات وب را در مرورگر انجام داد. بنابراین، مثالی از یک پردازه چندنخی در این بخش ارائه شد. در ادامه مقاله «Thread چیست» به شرح مزایای پردازههای چندنخی پرداخته شده است.
مزایای پردازههای چندنخی چه هستند؟
همانطور که بیان شد، در پردازههای چندنخی وظایف به طور همزمان اجرا میشوند. این سازکار باعث افزایش سرعت اجرا و صرفهجویی در زمان میشود. در این بخش از مقاله «Thread چیست» با جزئیات بیشتری به شرح مزایای پردازههای چندنخی پرداخته شده است:
- پاسخدهی سریع: استفاده از پردازههای چندنخی باعث میشود علاوه بر متوقف شدن بخشی از برنامه و یا اجرای یک عملیات طولانی، برنامه متوقف نشود و همچنان به درخواستهای کاربر پاسخ دهد.
- به اشتراکگذاری منابع: به طور پیشفرض، Threadها حافظه و منابع پردازهای که به آن تعلق دارند را به اشتراک میگذارند. مزیت به اشتراکگذاری کد و دادهها این است که به یک اپلیکیشن اجازه میدهد در یک فضای آدرس مشترک چندین Thread مختلف برای وظایف گوناگون داشته باشد.
- صرفه اقتصادی: تخصیص حافظه و منابع برای ایجاد پردازهها هزینهبر است. زیرا باید برای هر پردازه حافظه و منابع اختصاصی در نظر گرفت. اما، به جای ایجاد یک پردازه جدید برای هر وظیفه، با استفاده از سازکار چندنخی در پردازهها، Threadها منابع پردازه را با هم به اشتراک میگذارند و به این ترتیب، استفاده از چند Thread در پردازهها صرفه اقتصادی به همراه دارد.
- امکان استفاده از معماری چندپردازندهای: مزایای استفاده از سازکار چندنخی در بستری با معماری چندپردازنده میتواند به میزان زیادی افزایش یابد. در یک معماری با چندپردازنده، Threadها در پردازندههای مختلف به صورت موازی اجرا میشوند. اما، حتی اگر چند هسته یا چند پردازنده وجود داشته باشد، پردازه تکنخی تنها میتواند روی یک CPU اجرا شود. بنابراین، چندنخی در یک سیستم چندپردازنده همزمانی را افزایش میدهد.
حال پس از بیان مزایای عمده چندنخی، ادامه بخش مربوط به Multithreading در مطلب «Thread چیست» به بیان معایب چندنخی اختصاص داده شده است.
معایب Multithreading چیست؟
واضح است که چندنخی یا Multithreading نسبت به استفاده از روش تکنخی در پردازهها کاملاً برتری دارد و روشی جایگزین به حساب میآید، اما این روش مشکلاتی را نیز به همراه دارد. در صورت استفاده از چندنخی باید شناختی از چالشهای پیش رو وجود داشته باشد. بنابراین، در این بخش از مقاله «Thread چیست» دو تا از معایب Multithreading به طور خلاصه بیان شده است:
- پیچیدگی در برنامه نویسی: در پیادهسازی چندنخی، Threadهای بسیاری به طور همزمان روی دادهها، اشیا و توابع یکسان اجرا خواهند شد. در صورتی که کدنویسی به خوبی انجام نشود، مشکلات بسیاری به وجود میآید. در واقع، نسبت به حالت تکنخی، موارد بیشتری باید در نظر گرفته و کدنویسی آنها انجام شود.
- دشوار بودن تست و عیبیابی: با یک Thread ساده به راحتی میتوان تست و خطایابی را انجام داد. اما در صورت اجرای موازی Threadهای یک پردازه، این فرآیند بسیار پیچیدهتر است.
ادامه مطلب «Thread چیست» به شرح کاربردهای Thread اختصاص دارد.
کاربردهای Thread چه هستند؟
Threadها به بخش حیاتی از محاسبات بدل شدهاند و به پردازنده اجازه میدهند تا چندین وظیفه را به طور همزمان اجرا کند. بنابراین، کاربرد Thread منجر به بهبود سرعت انجام وظایف خواهد شد و امکان انجام چند وظیفه در لحظه برای کامپیوترها فراهم میشود. در واقع، این به واسطه وجود Threadها است که میتوان به صورت همزمان صفحات وب را مرور کرد و به موسیقی هم گوش داد.
Threadهای یک پردازه میتوانند اشیای سراسری آن پردازه، از جمله متغیرهای سراسری را با هم به اشتراک بگذارند. این اشتراکگذاری معمولاً از طریق دسترسی کنترل شده با استفاده از اشیایی انجام میشود که تنها میتوانند توسط یک Thread در لحظه استفاده شوند. کاربردهای Thread در ادامه این بخش از مقاله «Thread چیست» فهرست شدهاند:
- یک Thread میتواند تحت شرایطی بدون متوقف کردن سایر Threadها به هر دلیلی از جمله بروز خطا متوقف شود. بنابراین با استفاده از Thread احتمال و امکان از کار افتادن برنامه کاهش مییابد.
- استفاده از Thread این امکان را برای برنامه به وجود میآورد تا بتواند از چندین پردازنده یا چندین هسته پردازنده در کامپیوتر بهرهمند شود.
- Threadها امکان اجرای بخشهای منطقی مستقل یک برنامه را با کمترین میزان تلاش فراهم میکنند. برای مثال، در صورتی که قصد نمایش اطلاعات آماری به دفعات وجود داشته باشد، میتوان یک Thread برای انجام این وظیفه ایجاد کرد. در حالی که، انجام این کار به صورت دستی و به وسیله افزودن منطق به حلقه اصلی برنامه میتواند بسیار دشوار و دردسرساز باشد.
یکی دیگر از سوالات و ابهامات رایج پیرامون مفهوم Thread، تفاوت آن با Core یا هسته پردازنده است. بنابراین، ادامه نوشته «Thread چیست» به بیان تفاوتهای Core و Thread در پردازندهها اختصاص دارد.
تفاوت Core و Thread در پردازنده چیست؟
هستهها یا Coreها میزان کار انجام شده در واحد زمان را افزایش میدهند، در حالی که Threadها توان عملیاتی یا سرعت محاسبات را بهبود میبخشند. هسته یک قطعه سختافزاری واقعی است در حالی که Thread ماهیتی مجازی دارد و جزئی است که وظایف را مدیریت میکند. هستهها از تغییر زمینه (Context Switching) استفاده میکنند، اما Threadها چندین CPU را برای هدایت پردازههای متعدد به کار میگیرند.
در خصوص تفاوتهای Core و Thread سردرگمی بسیاری وجود دارد. برای درک عمیقتر تفاوتهای Core و Thread باید درکی از چیستی مفاهیم تکهستهای و چندهستهای و دلیل نیاز به آنها وجود داشته باشد. بنابراین در ادامه این بخش از مقاله «Thread چیست» معرفی اجمالی از دو مفهوم تکهستهای و چندهستهای و تفاوتهای آنها ارائه شده است.
پردازنده Single Core و Multi Core
به بیان ساده، یک CPU تکهستهای تنها میتواند یک پردازه و برنامه را در لحظه اجرا کند. اگرچه وقتی چندین برنامه به طور همزمان اجرا میشوند، یک پردازنده تکهستهای همه برنامهها را به قطعات کوچکتر تقسیم میکند و این قطعات را با استفاده از روش برش زمانی (Time Slicing) به صورت همزمان اجرا میکند. همزمانی در اینجا در یک بازه زمانی بزرگتر نمود پیدا میکند. در واقع قطعات پردازهها به طور همزمان اجرا نمیشوند، بلکه با درنظر گرفتن کل پردازه میتوان گفت اجرا به صورت همزمان انجام میشود. رویکرد برش زمانی در تصویر زیر نمایش داده شده است.
در تصویر فوق، محور افقی نماینده زمان و محور عمودی پردازههای برنامهها را در CPU نمایش میدهد. همانطور که ملاحظه میشود، هر پردازه به بخشهای مساوی تقسیم شده و در بازههای زمانی برابر بر اساس اولویتهای تعیین شده اجرا میشوند تا زمانی که اجرای پردازه به اتمام برسد. میزان عملکرد و کارایی CPU به تعداد هستههای آن و سرعت اجرای دستورالعملها توسط هر هسته بستگی دارد.
مشکل اصلی پردازنده تکهستهای چیست؟
به طور کلی دو مشکل اساسی در پردازندههای تکهستهای وجود دارد:
- برای اجرای سریعتر وظایف، نیاز به افزایش زمان کلاک CPU وجود دارد.
- افزایش زمان کلاک منجر به افزایش مصرف انرژی و تولید گرما به میزان بسیار زیاد میشود. این مسئله به ناکارآمدی CPU میانجامد.
امروزه تقریباً هیچ کامپیوتری با پردازنده تکهستهای تولید نمیشود، زیرا برنامهها سنگینتر و پیچیدهتر شدهاند و نیاز به توان محاسباتی و سرعت عمل بسیار بیشتری نسبت به گذشته وجود دارد. برخلاف پردازش تکهستهای، در یک CPU چند هستهای زیربخشهای هر پردازه به صورت همزمان اجرا میشوند. بازنمودی از این رویکرد در تصویر زیر بروز داده شده است:
به اجرای وظایف در یک پردازنده Multi Core، «اجرای موازی» هم گفته میشود، زیرا تمام زیر وظیفهها به صورت موازی اجرا میشوند. پیشتر به مشکلات CPUهای تکهستهای اشاره شد، حال در ادامه مطلب «Thread چیست» به این مسئله پرداخته شده است که پردازندههای چندهستهای چه راهکارهایی برای رفع تقاط ضعف پردازندههای Single Core ارائه میدهند؟
پردازنده چند هستهای چه راهکارهایی ارائه میدهد؟
پیشتر به کاستیهای پردازندههای چندهستهای اشاره شد. حال در این بخش به طور خلاصه راهکارهای استفاده شده برای بهبود مشکلات CPUهای تکهستهای در پردازندههای چندهستهای فهرست شدهاند:
- ایجاد دو هسته یا بیشتر از دو هسته در CPU علاوه بر افزایش قدرت پردازشی، سرعت کلاک CPU را نیز در سطح بهینه حفظ میکند.
- یک پردازنده با دو هسته که در سرعت بهینهای اجرا میشود، میتواند دستورالعملها را با سرعت برابری نسبت به یک پردازنده تکهستهای اجرا کند. اما چون سرعت کلاکش دو برابر است، پردازش چندهستهای انرژی کمتری مصرف میکند و در نتیجه گرمای کمتری تولید شده و اختلالی در عملکرد CPU به وجود نمیآید.
در شرح پردازندههای تکهستهای و چندهستهای به دو مفهوم همزمانی و موازی بودن اشاره شد. در ادامه مطلب «Thread چیست» توضیحات جامعتری پیرامون تفاوتهای Concurrency و Parallelism ارائه شده است. زیرا این دو مفهوم در بحث سیستمهای عامل با هم متفاوت هستند.
تفاوت Concurrency و Parallelism چیست ؟
دو اصطلاح Concurrency و Parallelism اغلب در ارتباط با برنامههای چندنخی به کار برده میشوند. Concurrency به معنای همزمانی و Parallelism به معنی اجرای موازی است. در ابتدا ممکن است این طور به نظر برسد که این دو اصطلاح به مفاهیم یکسانی اشاره دارند.
اما، همزمانی و موازیسازی دارای معانی متفاوتی هستند. در این بخش از مقاله «Thread چیست» به شرح دو مفهوم Concurrency و Parallelism و بیان تفاوتهای آنها پرداخته شده است.
Concurrency یا همروندی چیست؟
Concurrency یا همروندی یعنی یک برنامه (اپلیکیشن) در حال اجرا شدن با بیش از یک وظیفه (Task) در لحظه است. در صورتی که کامپیوتر تنها دارای یک CPU باشد، ممکن است اپلیکیشن نتواند دقیقاً در یک لحظه خاص بیش از یک وظیفه را پیش ببرد، اما در لحظه بیش از یک وظیفه داخل اپلیکیشن در حال اجرا است.
برای اجرای بیش از یک وظیفه به صورت همزمان، CPU در طول اجرا بین وظایف مختلف سوئیچ میکند. یعنی در یک لحظه بخشی از یک وظیفه و در لحظه بعد بخشی از وظیفه دیگر را انجام میدهد. این مفهوم در تصویر زیر نشان داده شده است:
بنابراین تا اینجا مفهوم اجرای همزمان تا حدودی مشخص شد. حال در ادامه مقاله «Thread چیست» به شرح مفهوم اجرای موازی پرداخته شده است.
اجرای موازی چیست؟
اجرای موازی (Parallel Execution) وقتی انجام میشود که یک کامپیوتر دارای بیش از یک CPU یا هسته CPU باشد و به معنای واقعی بیش از یک وظیفه را در لحظه انجام دهد. اگرچه، اجرای موازی به پدیده Parallelism اشاره نمیکند و در ادامه به Parallelism پرداخته خواهد شد. پیشتر در بخش پردازنده چندهستهای به اجرای موازی اشاره شده است. اما مفهوم دیگری به نام «اجرای همزمان موازی» نیز وجود دارد که در ادامه این بخش از مطلب «Thread چیست» به شرح آن پرداخته شده است.
اجرای همزمان موازی
میتوان شیوه اجرای موازی همزمان (Parallel Concurrent Execution) را نیز زمانی به کار گرفت که Threadها میان چندین CPU توزیع شده باشند. بنابراین، Threadهای اجرا شده روی یک CPU خاص به صورت همزمان و Threadهای مستقر در CPUهای متفاوت به صورت موازی اجرا خواهند شد. این پدیده در نمودار زیر به تصویر کشیده شده است:
مفهوم Parallelism چیست؟
اصطلاح Parallelism به این معنی است که یک اپلیکیشن وظایف خود را به تعدادی زیر وظیفه کوچکتر تقسیم میکند که میتوان آنها را به صورت موازی مثلاً در چند CPU دقیقاً در یک لحظه یکسان پردازش کرد. بنابراین، Parallelism به مدل اجرایی «همزمان موازی» که پیش از این معرفی شد ارتباطی ندارد، اگرچه ممکن است این دو اصطلاح در ظاهر مشابه به نظر برسند. برای دستیابی به Parallelism واقعی، اپلیکیشن باید بیش از یک Thread اجرایی در لحظه داشته باشد و هر Thread باید روی یک CPU مجزا، هستههای مختلف CPU، هستههای کارت گرافیک یا موارد مشابه اجرا شود.
در تصویر زیر یک وظیفه بزرگتر وجود دارد که به چهار زیربخش تقسیم شده است. این چهار زیربخش در حال اجرا به وسیله چهار Thread مختلف روی دو CPU مجزا هستند. این بدین معنا است که بخشی از این زیروظیفهها به صورت همزمان اجرا میشوند (آن زیروظیفههایی که در CPUهای یکسان قرار دارند) و تعدادی از آنها که در CPUهای متفاوتی مستقر هستند به صورت موازی اجرا میشوند:
در صورتی که این چهار زیر وظیفه هر کدام روی CPU مختص به خود اجرا میشدند (جمعاً چهار CPU) آنگاه اجرای وظایف به طور کامل موازی محسوب میشد. اگرچه، تقسیمبندی یک وظیفه و تبدیل آن به تعدادی زیر وظیفه که دقیقاً برابر با تعداد CPUها باشند همیشه کار چندان سادهای نیست، اغلب بهتر است تقسیمبندی یک وظیفه به قطعات کوچکتر را متناسب و سازگار با آن وظیفه انجام داد و سپس کار توزیع Threadها بین پردازندههای در دسترس را به زمانبند رشته (Thread Scheduler) سپرد. «Green Thread» یکی از اصطلاحات رایجی است که در خصوص Threadها به کار برده میشود و برای بسیاری این سوال وجود دارد که Green Thread چیست؟ بنابراین در ادامه مطلب «Thread چیست» به شرح چیستی آن پرداخته شده است.
Green Thread چیست؟
در برنامه نویسی «Threadهای سبز» (Green Threads) یا «Threadهای مجازی» Threadهایی هستند که به جای زمانبندی بومی توسط سیستمعامل زیربنایی به وسیله یک کتابخانه زمان اجرا یا ماشین مجازی زمانبندی میشوند. Threadهای سبز محیطهايی با چند Thread را بدون تکیه بر امکانات بومی سیستم عامل شبیهسازی میکنند. این نوع از Threadها به جای محیط کرنل در محیط کاربر مدیریت میشوند. این مسئله Threadهای سبز را قادر میسازد تا در محیطهایی کار کنند که دارای قابلیت پشتیبانی از Threadهای بومی نیستند. Green Thread ریشه در نام کتابخانه Thread در زبان برنامه نویسی جاوا دارد.
کتابخانه Thread در جاوا تا نسخه ۱.۱ منتشر و سپس در نسخه ۱.۳ رها و از Threadهای بومی استفاده شد. این کتابخانه توسط «گروه سبز» در شرکت سان مایکروسیستمز طراحی شده است. در یک پردازنده چندهستهای با اجرای Threadهای بومی میتوان به صورت خودکار وظایف را به چند پردازنده واگذار کرد، در حالی که با استقرار Threadهای سبز نمیتوان به این مهم دست پیدا کرد. اما، Threadهای سبز میتوانند در برخی از ماشینهای مجازی با سرعت بیشتری اجرا شوند. اگرچه، در کامپیوترهای تکپردازنده هنوز بهینهترین مدل به طور واضح مشخص نشده است. سنجههای انجام شده در کامپیوترهایی با هسته لینوکس نسخه ۲.۲ (مدتها منقضی شده) نشان دادهاند که:
- Threadهای سبز نسبت به Threadهای بومی لینوکس در فعالسازی و همگامسازی Threadها عملکرد بهتری دارند.
- Threadهای بومی لینوکس در خصوص عملیات ورودی/خروجی و تغییر زمینه (Context Switching) به میزان قابل توجهی عملکرد بهتری را از خود نشان میدهند.
وقتی که یک Thread سبز فراخوانی سیستم مسدود کننده را اجرا میکند، نه تنها آن Thread مسدود میشود، بلکه تمام Threadهای درون آن پردازه نیز مسدود میشوند. برای جلوگیری از چنین مشکلی Threadهای سبز باید از عملیات ورودی/خروجی ناهمگون استفاده کنند. اگرچه، در صورتی که ماشین مجازی اجرا کننده Threadهای سبز برخی از پردازههای ورودی/خروجی خاص (پنهان شده از کاربر) را نیز برای هر عملیات ورودی/خروجی اجرا کند، پیچیدگی افزوده در سمت کاربر میتواند کاهش یابد. همچنین، سازکارهایی وجود دارد که امکان استفاده از Threadهای بومی و کاهش سربار (Overhead) فعالسازی Thread و همگامسازی را فراهم میکنند:
- استخرهای نخ یا Thread Pools هزینه بسط دادن یک Thread جدید را به وسیله استفاده مجدد از تعدادی Thread فعلی کاهش میدهند. در بخشهای بعدی مقاله «Thread چیست» توضیحات بیشتری پیرامون چیستی Thread Pool ارائه شده است.
- زبانهای به کار گیرنده ماشینهای مجازی و Threadهای بومی میتوانند از «تجزیه و تحلیل فرار» (Escape Analysis) برای جلوگیری از همگامسازی بلوکهای کد در صورت عدم نیاز به آنها استفاده کنند.
در ادامه مقاله «Thread چیست» به شرح مفهوم Threading پرداخته شده است.
معنی Threading چیست؟
Threading یک فرآیند کم حجم است که توالی کدها و تمام ساختارهای پشتیبانی داده را نظیر منابع باز، نقشه حافظه، پشته و سایر موارد اجرا میکند. از Threading برای اجرای کدها به صورت موازی استفاده میشود. Threading از معماری چند پردازنده بهره میبرد و همچنین میتواند چند پردازه یا چند Thread را در قالب یک Process پردازش کند. Threading روشی است که در برنامه نویسی به کار گرفته میشود. بنابراین، در ادامه مقاله «Thread چیست» به چیستی Thread در برنامه نویسی اشاره شده است.
Thread در برنامه نویسی چیست ؟
همانطور که پیشتر بیان شد، میتوان در هر هسته یک پردازنده دو Thread ایجاد کرد و Threadها به طور موازی اجرا میشوند. در برنامه نویسی میتوان برای بهبود عملکرد و سرعت برنامه، هر کدام از وظایف و بخشهای مختلف یک اپلیکیشن را به یک Thread سپرد. هر Thread در برنامه نویسی دادههای ورودی را دریافت میکند، توالی از دستورات را انجام میدهد و خروجی تولید شده را باز میگرداند. در صورت لزوم میتوان به یک Thread خاص در پردازنده اولویت داد. به عنوان مثال در برنامه نویسی و توسعه یک اپلیکیشن موبایل، توسعهدهنده انتظار دارد رابط کاربری اپلیکیشن سریع و واکنشی باشد. برای انجام این کار میتوان عملیات مربوط به رابط کاربری را به یک Thread خاص سپرده و آن را در اولویت قرار داد.
به این ترتیب، Thread مربوطه همواره بر سایر Threadها تقدم خواهد داشت. همچنین میتوان از Thread با کمترین اولویت در زمانی استفاده کرد که Threadهای با اولویت بالاتر نیاز چندانی به منابع ندارند. از Threadهای با اولویت پایین میتوان برای انجام وظایفی مثل مراجعه به اینترنت برای دریافت اطلاعات، نوشتن دادهها و سایر کارهای نه چندان مهم استفاده کرد. بنابراین، Threading روشی در برنامه نویسی است که با استفاده از آن وظایف کم اهمیتتر در Threadهای با اولویت کمتر اجرا میشوند که به آن Thread پس زمینه یا Background Thread نیز گفته میشود و سپس وقتی که قصد انجام وظیفهای با اولویت بالاتر وجود داشته باشد، این وظیفه در Thread پیشزمینه یا Foreground Thread انجام خواهد شد. به این ترتیب، مفهوم Thread در برنامه نویسی شرح داده شد. حال در ادامه مطلب «Thread چیست» به شرح مفهوم Thread و آموزش استفاده از آن در چند زبان برنامه نویسی رایج پرداخته شده است.
Thread در جاوا چیست؟
یک Thread در جاوا مسیری است که در اجرای یک برنامه دنبال میشود. تمام برنامههای جاوا حداقل دارای یک Thread هستند که به آن Thread اصلی میگویند. Thread اصلی توسط ماشین مجازی جاوا (JVM) در آغاز برنامه و در زمان فراخوانی متد main() با Thread اصلی ایجاد میشود. یک Thread را در جاوا میتوان به وسیله پیادهسازی یک واسط و بسط کلاس ایجاد کرد.
تمام نخهای جاوا به وسیله کلاس «java.lang.Thread» ایجاد و کنترل میشوند. یک اپلیکیشن تکرشتهای (Single Thread) تنها دارای یک Thread است و میتواند در لحظه تنها یک وظیفه را انجام دهد. برای مدیریت چند وظیفه به صورت موازی از Multithreading یا همان چندنخی استفاده میشود که در بخشهای قبلی به آن پرداخته شد. تصویر زیر نحوه پردازش Threadها در JVM را نشان میدهد.
Threadها در جاوا به یک برنامه اجازه میدهند تا با انجام چندین کار در یک لحظه به طور موثرتری عمل کنند. از Threadها در جاوا میتوان برای اجرای وظایف پیچیده در پسزمینه بدون ایجاد وقفه در برنامه اصلی استفاده کرد. در ادامه پاسخ به چیستی Thread در جاوا از مطلب «Thread چیست» نحوه ایجاد Thread در جاوا آموزش داده شده است.
ایجاد یک Thread در جاوا
دو راه برای ایجاد یا آغاز یک نخ در جاوا وجود دارد. میتوان از بسط کلاس Thread و باطل کردن متد run() در آن استفاده کرد:
1public class Main extends Thread {
2 public void run() {
3 System.out.println("This code is running in a thread");
4 }
5}
راه دیگری که برای ایجاد یک Thread وجود دارد، پیادهسازی واسط Runnable به صورت زیر است:
1public class Main implements Runnable {
2 public void run() {
3 System.out.println("This code is running in a thread");
4 }
5}
حال در ادامه مطلب «Thread چیست» به شرح نحوه اجرای Threadها در جاوا پرداخته شده است:
اجرای Thread در جاوا
در صورتی که کلاس مربوطه از کلاس Thread مشتق شده باشد، Thread میتواند به وسیله ایجاد یک نمونه از کلاس و فراخوانی متد start() در آن اجرا شود. در ادامه مثالی در این خصوص ارائه شده است:
1public class Main extends Thread {
2 public static void main(String[] args) {
3 Main thread = new Main();
4 thread.start();
5 System.out.println("This code is outside of the thread");
6 }
7 public void run() {
8 System.out.println("This code is running in a thread");
9 }
10}
حال در صورتی که کلاس به وسیله پیادهسازی واسط Runnable ایجاد شده باشد، میتوان Thread را با ارجاع یک نمونه از کلاس (ایجاد یک شی از کلاس) به یک سازنده شی (Constructor) و سپس فراخوانی متد start() اجرا کرد. مثالی از این روش ایجاد Thread در جاوا به صورت زیر است:
1public class Main implements Runnable {
2 public static void main(String[] args) {
3 Main obj = new Main();
4 Thread thread = new Thread(obj);
5 thread.start();
6 System.out.println("This code is outside of the thread");
7 }
8 public void run() {
9 System.out.println("This code is running in a thread");
10 }
11}
مشکلات همروندی در Threadهای جاوا
به دلیل اینکه Threadها به طور همزمان با سایر بخشهای برنامه اجرا میشوند، به هیچ طریقی نمیتوان متوجه شد که کدها با چه ترتیبی اجرا خواهند شد. وقتی که Threadها و برنامه اصلی در حال خواندن و نوشتن متغیرهای یکسانی هستند، مقادیر این متغیرها غیرقابل پیشبینی است.
مشکلاتی که از این مسئله منتج میشود را مشکلات همزمانی یا Concurrency Problems میگویند. در ادامه مثالی آمده است که در آن مقدار متغیر «amount» به دلیل بروز مشکل همزمانی غیرقابل پیشبینی است:
1public class Main extends Thread {
2 public static int amount = 0;
3
4 public static void main(String[] args) {
5 Main thread = new Main();
6 thread.start();
7 System.out.println(amount);
8 amount++;
9 System.out.println(amount);
10 }
11
12 public void run() {
13 amount++;
14 }
15}
برای جلوگیری از مشکلات همزمانی، بهترین راه این است که در صورت امکان چند صفت (Attribute) بین Threadها به اشتراک گذاشته شود. در صورتی که نیاز باشد صفتها به اشتراک گذاشته شوند، یک راهکار استفاده از متد isAlice() است تا بررسی شود که آیا اجرای Thread پیش از استفاده از هر یک از صفتهایی که Thread میتواند تغییر دهد خاتمه پیدا کرده است یا خیر. در مثال زیر از isAlive() برای جلوگیری از مشکلات همزمانی استفاده شده است:
1public class Main extends Thread {
2 public static int amount = 0;
3
4 public static void main(String[] args) {
5 Main thread = new Main();
6 thread.start();
7 // Wait for the thread to finish
8 while(thread.isAlive()) {
9 System.out.println("Waiting...");
10 }
11 // Update amount and print its value
12 System.out.println("Main: " + amount);
13 amount++;
14 System.out.println("Main: " + amount);
15 }
16 public void run() {
17 amount++;
18 }
19}
Daemon Thread در جاوا چیست؟
Daemon Thread یک Thread با اولویت پایین است که برای اجرای وظایفی نظیر زبالهروبی (پاک کردن خانههای بلااستفاده حافظه | Garbage Collection) استفاده میشود. Daemon Thread نوعی Thread است که از بسته شدن JVM در حالتی که برنامه تمام شده اما Thread همچنان در حال اجرا است جلوگیری نمیکند.
در واقع JVM تنها وقتی بسته و خارج میشود که Threadهای Daemon باقی مانده و سایر Threadها اجرا شده باشند. با فراخوانی متد setDaemon() میتوان یک Daemon Thread ایجاد کرد. حال پس از بررسی چیستی نخ در جاوا، ادامه مطلب «Thread چیست» به بیان شرحی از نخها در اندروید اختصاص داده شده است.
Thread در اندروید چیست ؟
یک Thread در اندروید نیز رشتهای اجرایی در یک برنامه به حساب میآید. ماشین مجازی جاوا در اندروید نیز به یک اپلیکیشن اجازه میدهد تا چندین Thread اجرایی داشته باشد و به صورت همزمان اجرا شوند. هر Thread دارای یک میزان اولویت مشخص است. یک Thread ممکن است به عنوان «Daemon» نشانهگذاری شود.
وقتی که کدهای اجرا شده در Thread یک شی Thread جدید ایجاد کنند، اولویت Thread جدید با Thread ایجاد کننده آن برابر خواهد بود و در صورتی که Thread ایجاد کننده Daemon باشد، Thread جدید نیز Daemon خواهد بود. وقتی که یک ماشین مجازی جاوا شروع به کار میکند، معمولاً یک Thread غیر Daemon واحد ایجاد میشود که اغلب متد main از یک کلاس تعیین شده را فراخوانی میکند. ماشین مجازی جاوا تا زمانی که هر یک از موارد زیر رخ بدهد، به اجرای Threadها ادامه خواهد داد:
- متد «exit» از کلاس «Runtime» فراخوانی شده و مدیر امنیت مجوز رخداد عملیات خروج را صادر کرده باشد.
- همه Threadها به غیر از Threadهای Daemon یا به وسیله بازگرداندن فراخوانی به متد «run» از بین رفته باشند یا به وسیله ایجاد یک استثنا که برای متد run تکثیر میشود.
بنابراین، با توجه به اینکه اغلب در برنامه نویسی اندروید از زبان جاوا استفاده میشود، میتوان Thread در اندروید را همان Thread در جاوا در نظر گرفت. به این ترتیب، برای آموزش بیشتر و دیدن مثالها در قالب کد میتوان به بخش Thread در جاوا در همین نوشته مراجعه کرد که پیشتر آمده است. در ادامه مقاله «Thread چیست» به شرح استفاده از Thread در زبان برنامه نویسی سیپلاسپلاس پرداخته شده است.
Thread در سی پلاس پلاس
در این بخش از مقاله «Thread چیست» به نحوه ایجاد و استفاده از Thread در زبان برنامه نویسی C++ پرداخته شده است.
ایجاد Thread در سی پلاس پلاس
برای آموزش ایجاد Thread در سی پلاس پلاس از نمونه کد زیر استفاده شده است:
1#include <iostream>
2#include <thread>
3
4void thread_function()
5{
6 std::cout << "thread function\n";
7}
8
9int main()
10{
11 std::thread t(&thread_function); // t starts running
12 std::cout << "main thread\n";
13 t.join(); // main thread waits for the thread t to finish
14 return 0;
15}
این کد در یک سیستم لینوکسی به صورت زیر اجرا و خروجی آن چاپ میشود:
$ g++ t1.cpp -o t1 -std=c++11 -pthread $ ./t2 thread function main thread
اولین کاری که باید انجام داد، ایجاد یک شی Thread یا همان Thread کارگر (Worker Thread) و محول کردن کاری در قالب یک تابع به آن Thread برای انجام دادن است. Thread اصلی منتظر یک Thread میماند تا با موفقیت به اتمام برسد. به همین دلیل، از تابع join() استفاده شده است. در صورتی که Thread اصلی و اولیه برای تمام شدن Thread جدید صبر نمیکرد، این Thread تا پایان main() ادامه پیدا میکرد و پیش از آنکه Thread جدید فرصتی برای اجرا داشته باشد، برنامه به اتمام میرسید.
Thread اصلی در زمان انتظارش در حالت بیکار (Idle) قرار دارد و در واقع سیستم عامل ممکن است منابع CPU را از Thread اصلی باز پس گیرد. باید در نظر داشت که یک سربرگ جدید کتابخانه استاندارد C++ به صورت include <thread># ارائه شده است که توابع و کلاسهای مربوط به Thread در آن تعریف شدهاند. جریان اجرایی Threadها در C++ مشابه نمودار زیر است:
اگرچه، در دنیای واقعی شرایط ایدهآل نیست و جریان اجرایی به احتمال زیاد نامتقارن و بیشتر مشابه تصویر زیر خواهد بود:
در حالی که Thread کارگر از طریق سازنده «std::thread t» آغاز شده، ممکن است در ایجاد یک Thread سربار وجود داشته باشد. این سربار میتواند با استفاده از اتسخر نخ کاهش یابد. خطچین نشان دهنده یک وضعیت قفل شده است. حال در ادامه مطلب «Thread چیست» به استفاده از Thread در زبان سی شارپ پرداخته شده است.
Thread در سی شارپ
در سی شارپ، کلاس «System.Threading.Thread» برای کار با Threadها مورد استفاده قرار میگیرد. این کلاس امکان ایجاد و دسترسی به هر یک از Threadهای اپلیکیشنهای چندنخی را فراهم میکند. همانطور که پیشتر بیان شد، اولین نخی که باید در این فرایند اجرا شود، Thread اصلی یا Main Thread نام دارد.
زمانی که اجرای یک برنامه سی شارپ شروع میشود، Thread اصلی به صورت خودکار ایجاد میشود. Threadهایی که با استفاده از کلاس Thread ایجاد میشوند، Threadهای فرزند نام دارند. میتوان با استفاده از خصوصیت CurrentThread از کلاس Thread به یک Thread دسترسی پیدا کرد. برنامه زیر اجرا Thread اصلی را نشان میدهد:
1using System;
2using System.Threading;
3
4namespace MultithreadingApplication {
5 class MainThreadProgram {
6 static void Main(string[] args) {
7 Thread th = Thread.CurrentThread;
8 th.Name = "MainThread";
9
10 Console.WriteLine("This is {0}", th.Name);
11 Console.ReadKey();
12 }
13 }
14}
زمانی که کدهای فوق کامپایل و اجرا شوند، خروجی زیر تولید خواهد شد:
This is MainThread
ایجاد Thread در سی شارپ
نخها در سی شارپ با بسط دادن کلاس Thread ایجاد میشوند. سپس، کلاس Thread بسط داده شده متد Start() را برای آغاز اجرای Thread فراخوانی میکند. برنامه زیر این مفهوم را نشان میدهد:
1using System;
2using System.Threading;
3
4namespace MultithreadingApplication {
5 class ThreadCreationProgram {
6 public static void CallToChildThread() {
7 Console.WriteLine("Child thread starts");
8 }
9 static void Main(string[] args) {
10 ThreadStart childref = new ThreadStart(CallToChildThread);
11 Console.WriteLine("In Main: Creating the Child thread");
12 Thread childThread = new Thread(childref);
13 childThread.Start();
14 Console.ReadKey();
15 }
16 }
17}
پس از کامپایل و اجرای کدهای فوق، نتیجه زیر در خروجی چاپ میشود:
In Main: Creating the Child thread Child thread starts
مدیریت Threadها در سی شارپ
کلاس thread متدهای بسیاری ار برای مدیریت Threadها فراهم میکند. مثال زیر استفاده از متد sleep() را برای متوقف کردن موقتی یک Thread در مدت زمانی مشخص نشان میدهد:
1using System;
2using System.Threading;
3
4namespace MultithreadingApplication {
5 class ThreadCreationProgram {
6 public static void CallToChildThread() {
7 Console.WriteLine("Child thread starts");
8
9 // the thread is paused for 5000 milliseconds
10 int sleepfor = 5000;
11
12 Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000);
13 Thread.Sleep(sleepfor);
14 Console.WriteLine("Child thread resumes");
15 }
16
17 static void Main(string[] args) {
18 ThreadStart childref = new ThreadStart(CallToChildThread);
19 Console.WriteLine("In Main: Creating the Child thread");
20
21 Thread childThread = new Thread(childref);
22 childThread.Start();
23 Console.ReadKey();
24 }
25 }
26}
خروجی کدهای فوق به صورت زیر خواهد بود:
In Main: Creating the Child thread Child thread starts Child Thread Paused for 5 seconds Child thread resumes
حذف Threadها در سی شارپ
برای حذف و از بین بردن Threadها در سی شارپ از متد Abort() استفاده میشود. Thread با ایجاد یک استثنا به نام ThreadAbortException در زمان اجرا متوقف میشود. حذف Threadها در برنامه زیر نشان داده شده است:
1using System;
2using System.Threading;
3
4namespace MultithreadingApplication {
5 class ThreadCreationProgram {
6 public static void CallToChildThread() {
7 try {
8 Console.WriteLine("Child thread starts");
9
10 // do some work, like counting to 10
11 for (int counter = 0; counter <= 10; counter++) {
12 Thread.Sleep(500);
13 Console.WriteLine(counter);
14 }
15
16 Console.WriteLine("Child Thread Completed");
17 } catch (ThreadAbortException e) {
18 Console.WriteLine("Thread Abort Exception");
19 } finally {
20 Console.WriteLine("Couldn't catch the Thread Exception");
21 }
22 }
23 static void Main(string[] args) {
24 ThreadStart childref = new ThreadStart(CallToChildThread);
25 Console.WriteLine("In Main: Creating the Child thread");
26
27 Thread childThread = new Thread(childref);
28 childThread.Start();
29
30 //stop the main thread for some time
31 Thread.Sleep(2000);
32
33 //now abort the child
34 Console.WriteLine("In Main: Aborting the Child thread");
35
36 childThread.Abort();
37 Console.ReadKey();
38 }
39 }
40}
خروجی کدهای فوق به صورت زیر است:
In Main: Creating the Child thread Child thread starts 0 1 2 In Main: Aborting the Child thread Thread Abort Exception Couldn't catch the Thread Exception
Thread در پایتون
Threading در پایتون امکان اجرای همزمان برنامههای مختلف را پدید میآورد و میتواند منجر به سادهسازی طراحی شود. اگرچه برای اکثر پیادهسازیهای پایتون نسخه سه، Threadهای مختلف در واقع همزمان اجرا نمیشوند و تنها اینطور به نظر میرسد که اجرا همزمان است. ممکن است Threadها در پردازندههای متفاوتی اجرا شوند اما در هر لحظه یکی از آنها اجرا خواهد شد. البته این مسئله در همه زبانها و به طور کلی برای مفهوم همزمانی وجود دارد.
واداشتن چند وظیفه به اجرا به صورت همزمان نیازمند یک پیادهسازی غیراستاندارد از پایتون، نوشتن برخی از کدها به زبان دیگر یا استفاده از چندپردازندگی (Multiprocessing) دارد که مقداری سربار اضافی به همراه خواهد داشت. به دلیل نحوه کارکرد پیادهسازی CPython از زبان Python، ممکن است Threading منجر به افزایش سرعت تمام وظایف نشود. این مسئله به دلیل تعامل با «قفل مفسر سراسری» (GIL) رخ میدهد که لزوماً محدودیت اجرای یک Thread پایتون در لحظه را به وجود آورده است.
وظایفی که بیشتر زمان آنها برای رویدادهای خارجی صرف میشود، گزینههای خوبی برای Threading به حساب میآیند. مسائلی که نیاز به محاسبات CPU سنگینی دارند و زمان اندکی را برای رویدادهای خارجی به انتظار مینشینند ممکن است اجرای سریعتری نداشته باشند. این مسئله در مورد کدهای نوشته شده در پایتون و اجرا بر اساس پیادهسازی استاندارد CPython صدق میکند. در صورتی که Threadها به زبان C نوشته شده باشند، امکان رهاسازی GIL و اجرای همزمان را دارند.
در صورتی که از پیادهسازی پایتون متفاوتی استفاده میشود، باید مستندات آن را بررسی کرد تا نحوه مدیریت Threadها در آن مشخص شود. اگر معماری برنامه سازگار با Threadها طرحریزی شود، میتواند علاوه بر بهبود سرعت، شفافیت در طراحی را نیز به همراه داشته باشد. اگرچه ممکن است در اکثر موارد استفاده از Threading منجر به اجرای سریعتر نشود. حال در ادامه این بخش از مقاله «Thread چیست» به نحوه ایجاد یک نخ در پایتون پرداخته شده است.
ایجاد یک Thread در پایتون
کتابخانه استاندارد پایتون امکان Threading را ارائه میدهد. ایجاد یک Thread در پایتون به صورت زیر انجام میشود:
1import logging
2import threading
3import time
4
5def thread_function(name):
6 logging.info("Thread %s: starting", name)
7 time.sleep(2)
8 logging.info("Thread %s: finishing", name)
9
10if __name__ == "__main__":
11 format = "%(asctime)s: %(message)s"
12 logging.basicConfig(format=format, level=logging.INFO,
13 datefmt="%H:%M:%S")
14
15 logging.info("Main : before creating thread")
16 x = threading.Thread(target=thread_function, args=(1,))
17 logging.info("Main : before running thread")
18 x.start()
19 logging.info("Main : wait for the thread to finish")
20 # x.join()
21 logging.info("Main : all done")
در کدهای فوق در صورتی که به خط کدهای مربوط به logging توجه شود، میتوان ملاحظه کرد که بخش main در حال اجرا و شروع Thread است:
1x = threading.Thread(target=thread_function, args=(1,))
2x.start()
وقتی که برنامهنویس یک Thread ایجاد میکند، این Thread به عنوان یک تابع و لیستی شامل آرگومانهای آن تابع فراخوانی میشود. به این ترتیب تا به مفهوم Thread در چند زبان برنامه نویسی رایج پرداخته شد. حال در ادامه مقاله «Thread چیست» به شرح چند مفهوم و اصطلاح مرتبط با Thread در برنامه نویسی پرداخته شده است.
مفهوم Thread Safety
«Thread Safety» یا «امنیت Thread» یک مفهوم برنامه نویسی است که به کدهای Multithread مربوط میشود. کد Thread Safe ساختارهای داده اشتراکی را به گونهای دستکاری میکند که تمام Threadها به درستی رفتار و مشخصههای طراحی خود را بدون تعامل ناخواسته پیادهسازی و اجرا کنند. راهبردهای مختلفی برای ایجاد ساختارهای Thread Safe وجود دارد.
یک برنامه ممکن است کدهایی را در Threadهای بسیاری در یک فضای آدرس به صورت همزمان اجرا کند، به گونهای که هر یک از آن Threadها به طور موازی به تمام حافظه هر Thread دسترسی داشته باشند. Thread safety قابلیتی است که امکان اجرای کدها در فضاهای دارای چند Thread را با برقراری مجدد برخی از مکاتبات میان جریان کنترلی و متن برنامه از طریق همگامسازی فراهم میکند. تولید کنندگان مختلف از اصطلاحات متفاوتی برای امنیت Thread استفاده میکنند. سطوح مختلف امنیت Thread به شرح زیرند:
- Thread Safe: در صورتی که دسترسی به وسیله چند Thread به صورت همزمان اتفاق بیوفتد، در این سطح تضمین شده است که پیادهسازی عاری از وضعیت رقابتی (Race Condition) باشد.
- Conditionally Safe (ایمن با شرط): Threadهای مختلف میتوانند به صورت همزمان به اشیا مختلف دسترسی داشته باشند و دسترسی به دادههای اشتراکی از شرایط رقابتی حافظت میشوند.
- Not Thread Safe: نخهای مختلف نباید همزمان به ساختارهای داده دسترسی داشته باشند.
تضمینهای امنیت Thread معمولاً شامل گامهای طراحی برای جلوگیری یا محدودسازی ریسک اشکال مختلف بنبست به همراه بهینهسازیهایی برای بیشینه کردن کارایی همزمانی است. اگرچه، تضمینهای بدون بنبست همیشه قابل ارائه نیستند، زیرا بنبستها میتوانند به وسیله بازخوانیها (Callbacks) و نقض لایهبندی معماری مستقل از خود کتابخانه به وجود بیایند. دو دسته از رویکردها برای جلوگیری از وضعیتهای رقابتی و دستیابی به Thread Safety وجود دارد. دسته اول از رویکردها بر اجتناب از وضعیت اشتراکی تمرکز دارد. دومین دسته از رویکردها مرتبط با همگامسازی هستند و در شرایطی استفاده میشوند که وضعیت اشتراکی قابل اجتناب نیست. در ادامه شرح مفهوم Thread Safety در مطلب «Thread چیست» به بیان مثالهایی برای درک بهتر این مفهوم پرداخته شده است.
مثالهایی برای مفهوم امنیت Thread
در کدهای جاوای زیر، از کلمه کلیدی «Synchronized» برای ایمن کردن Thread استفاده شده است:
1class Counter {
2 private int i = 0;
3
4 public synchronized void inc() {
5 i++;
6 }
7}
در زبان برنامه نویسی C، هر Thread دارای پشته مربوط به خودش است. با وجود اینکه یک متغیر ایستا در پشته نگهداری نمیشود، تمام Threadها دسترسی همزمان به آن را با هم شریک میشوند. در صورتی که حین اجرای یک تابع چندین Thread با هم تداخل پیدا کنند، احتمال دارد که یک متغیر ایستا به وسیله یک Thread در حالی تغییر کند که Thread دیگری در حال بررسی آن است.
این خطای منطقی که تشخیص آن دشوار است و ممکن است در اکثر مواقع به درستی کامپایل و اجرا شود، یک وضعیت رقابتی خوانده میشود. معمولاً یک راه برای جلوگیری از بروز این وضعیت، استفاده از یک متغیر دیگر به عنوان «Lock» یا «Mutex» است. در کدهای زیر به زبان C، تابع Thread Safe است، اما دوباره وارد شونده (بازآینده | بازگذشتی | Reentrant) نیست:
1# include <pthread.h>
2
3int increment_counter ()
4{
5 static int counter = 0;
6 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
7
8 // only allow one thread to increment at a time
9 pthread_mutex_lock(&mutex);
10
11 ++counter;
12
13 // store value before any other threads increment it further
14 int result = counter;
15
16 pthread_mutex_unlock(&mutex);
17
18 return result;
19}
در کدهای فوق، «increment_counter» میتواند به وسیله Threadهای مختلف بدون هیچ مشکلی فراخوانی شود. زیرا برای همگامسازی تمام دسترسی به متغیر مشترک counter از یک mutex استفاده شده است. اما در صورتی که تابع در مدیر وقفه بازگذشتی به کار رود و یک وقفه دوم رخ دهد در حالی که mutex قفل باشد، دومین روال برای همیشه باقی خواهد ماند. چون سرویسدهی وقفه میتواند سایر وقفهها را غیرفعال کند، کل سیستم میتواند تحت تاثیر قرار بگیرد. همین تابع را میتوان در C++ نسخه ۱۱ به گونهای پیادهسازی کرد که هم Thread Safe و هم با استفاده از اتمیکهای بدون قفل بازگذشتی باشد:
1# include <atomic>
2
3int increment_counter ()
4{
5 static std::atomic<int> counter(0);
6
7 // increment is guaranteed to be done atomically
8 int result = ++counter;
9
10 return result;
11}
یکی دیگر از مفاهیم مربوط به Thread، اصطلاح «Thread Pool» است که پیشتر نیز در خلال مطالب بیان شده در مقاله «Thread چیست» به آن اشاره شد. حال در ادامه به شرح این مفهوم پرداخته شده است.
Thread Pool چیست؟
در برنامه نویسی، «Thread Pool» یا «الگوی مخزن Thread»، یک الگوی طراحی نرمافزار برای دستیابی به همزمانی در یک برنامه کامپیوتری است. به این الگوی طراحی «کارگران تکرار شوند» (Replicated Workers) یا مدل Worker-Crew نیز گفته میشود. یک مخزن Thread چندین Thread را نگهداری میکند و منتظر میماند تا وظایف برای اجرای همزمان توسط برنامه نظارت کننده تخصیص داده شوند.
عملکرد و کارایی مدل طراحی شده به وسیله نگهداری مخزنی از Threadها بهبود پیدا میکند و از بروز تاخیر در اجرا به دلیل ایجاد و تخریب مکرر Threadها برای وظایف موقت جلوگیری میشود. تعداد Threadهای در دسترس با منابع محاسباتی در دسترس برنامه مثل صف یک وظیفه موازی پس از اتمام اجرا تطبیق داده میشوند. به این ترتیب، اکثر مباحث و مفاهیم مرتبط با Threadها در این مقاله شرح داده شد. اکنون در بخش انتهایی به معرفی دورههای آموزشی مرتبط با Thread پرداخته شده است.
جمعبندی
در مقاله «Thread چیست» ابتدا به شرح چیستی نخ یا Thread پرداخته شد. برای آموزش مفهوم Thread به شرح سازکار و روال کارکرد Thread اجزای تشکیل دهنده آن پرداخته شد. با توجه به اینکه Thread در قالب Process یا پردازه کاربرد دارد، مفهوم Process نیز به طور جامع شرح داده شد. بسیاری از سوالات رایج پیرامون مفهوم Thread در این مقاله پوشش داده شده است.
برخی مفاهیم پوشش داده شده مرتبط با Thread در این مقاله شامل CPU، Hyper Threading، چندنخی یا Multithreading، همزمانی، اجرای موازی، Parallelism، نخهای سبز (Green Threads)، نخ امن (Safety Thread)، استخر نخ (Thread Pool) و سایر موارد است. همچنین در این مطلب چیستی Thread در برنامه نویسی شرح داده و مثالهایی در زبانهای برنامه نویسی مختلف ارائه شده است.
ممنون از شما بسیار عالی و شفاف … خسته نباشید
عالی