برنامه نویسی Concurrent شبیه مدیریت یک رستوران زنجیره ای با چندین شعبه است. همانطور که مدیریت چنین رستورانی، دانش خاص خود را نیاز دارد، Concurrent Programming نیز دانش خاص خود را نیاز دارد.
در این بخش قصد دارم مفاهیم Concurrent Programming را قالب یک رستوران زنجیره ای با چندین شعبه به شما آموزش دهم.
مثال – رستوران زنجیرهای
یک رستوران زنجیرهای را در نظر بگیرید.
- شما یک رستوران دارید. (شما یک کامپیوتر دارید)
- این رستوران ۴ شعبه دارد. (این کامپیوتر یک CPU با ۴ هسته پردازشی سخت افزاری دارد)
- هر شعبه دو آشپزخانه دارد. یکی برای طبخ غذا های چینی و دیگری برای طبخ غذا های ایتالیایی. (هر هسته سخت افزاری خودش به دو هسته نرم افزاری تقسیم میشود)
- هر کدام از آشپزخانه ها را یک سرآشپز به صورت کاملا مستقل مدیریت میکند. (هر پردازنده، پردازش خاصی را در یک لحظه اجرا میکند.)
- هر سرآشپز، گروهی از آشپز ها را مدیریت میکند. (هر آشپز معادل یک thread از یک پردازش است)
- هر گروه، ملزومات آشپزی خود را دارند. (هر پردازش، منابع سخت افزاری خاص خود را دارد که از منابع سایر پردازش ها کاملا مستقل است)
- هر سرآشپز، کمیت و کیفیت آشپز ها و ملزومات آشپزی را مدیریت میکند.
- این رستوران زنجیره ای یک مدیر اجرایی دارد که بر عملکرد همه شعب نظارت دارد و با توجه به پارامتر های گوناگون برای شعب و حتی برای آشپزخانه های رستوران استراتژی میچیند. مثلا اگر در یکی از شعب، سفارش غذا های ایتالیایی بیشتر باشد، بخشی از بودجه شعب دیگر را به این شعبه اختصاص میدهد. (کامپیوتر یک سیستم عامل دارد که مسئول اجرای بهینه و مدیریت برنامه هاست)

سیسیتمعامل چیست؟
سیستمعامل مانند آن مدیر اجرایی در مثال «رستورانهای زنجیرهای» عمل میکند که مسئول سیاست گذاری و مدیریت شعب است. مدیر اجرایی نمیداند یک غذا باید چطور طبخ شود اما اگر هر آشپزخانه یا سرآشپز به خوبی عمل نکنند متوجه شده و استراتژی های لازم را به کار میگیرد تا عملکرد رستوران بهتر شود.
سیستم عامل نرم افزاری است که واسطه بین سختافزار و سایر نرمافزار هاست. او از جزئیات برنامه هایی که اجرا میکنیم اطلاعی ندارد اما به خوبی میداند که منابع سخت افزاری را چگونه به برنامه ها اختصاص دهد که آنها بهترین عملکرد را داشته باشند.

ویندوز، لینوکس، مکینتاش، اندروید و IOS ازجمله معروفترین سیستمعامل ها هستند.
CPU چیست؟
CPU مغز کامپیوتر است. برخی از CPU ها یک و برخی چند هسته پردازنده دارند. در واقع این هستههای CPU هستند که با انجام محاسبات ریاضی امکان اجرای برنامهها را فراهم میکنند.
CPU مانند شعبههای آشپزخانه در مثال «رستورانهای زنجیرهای» است. در این مثال دیدیم که هر شعبه دو آشپزخانه دارد. هر آشپزخانه نمادی از یکی از هستههای CPU است.
Process چیست؟
هر برنامهای که اجرا میکنید، یک Process است. Process با نامهایی مانند پردازش یا پردازه نیز شناخته میشود.
در مثال «رستورانهای زنجیرهای» هر سرآشپز نمادی از یک Process است. همانطور که مدیر اجرایی برای هر آشپزخانه یک سرآشپز مشخص میکند، سیستمعامل نیز به هر هسته پردازنده از CPU یک پردازش تخصیص میدهد.
همانطور که هر گروه آشپزی ملزومات آشپزی خود را دارند، هر پردازش نیز ID، RAM و حافظه اختصاصی خود را دارد.

همانطور که هر سرآشپز مستقل از سایر سرآشپز ها کار میکند، هر پردازش نیز به صورت مستقل از سایر پردازشها کار میکند. مثلا، اگر تلگرام و گوگلکروم را همزمان اجرا کنید، این دو برنامه درک خاصی از یکدیگر ندارند و همچنین تاثیری در اجرای یکدیگر ندارند.
البته گاهی لازم است تا process ها چیز هایی را با هم به اشتراک بگذارند و پیام هایی بین خود تبادل کنند که برای این منظور نیست راه هایی در نظر گرفته شده است که من فعلا از پرداختن به آن صرف نظر میکنم.
Thread چیست؟
کوچکترین بخش از یک Process که میتوان پردازش کرد را thread میگویند. همانطور که هر سرآشپز میتواند با چندین آشپز چند غذا را به صورت همزمان آماده کند، هر Process نیز میتواند چند Thread داشته باشد و برنامه را به صورت همزمان در thread های مختلف پردازش کند.

برای فهم بهتر لطفا به مثالهای زیر نیز توجه کنید.
مثال – دانلودمنیجر
احتمالا حداقل یک بار از نرمافزار دانلود منیجر در ویندوز استفاده کردهاید. این نرمافزار میتواند یک فایل را به چند قسمت تقسیم کرده و همه را به صورت همزمان دانلود کند.
فرض کنید میخواهید یک فایل به نام ubuntu.iso را دانلود کنید. آیا میتوان دو بار دانلود منیجر را باز کرد و نصف فایل را با اولی و نصف دیگر را با دومی دانلود کرد؟
خیر، زیرا این هر کدام از این دانلودمنیجرها یک Process جداگانه هستند و هیچکدام از آنها به دیتاهای دیگری در لحظه اجرا دست پیدا نمیکنند.
اما میتوان همان یک فایل را به چند بخش تقسیم کرد و همه بخشها را به صورت همزمان در یکی از دانلودمنیجرهایی که باز است دانلود کرد.

در این مثال هر بخش از فایل که در حال دانلود است به عنوان یک thread میتواند از وضعیت سایر بخشها مطلع باشد تا در نهایت همه thread ها بتوانند بفهمند که دانلود فایل به پایان رسیده است یا نه.
در این مثال:
- هر کدام از دانلودمنیجرهای باز شده یک Process هستند.
- هر بخش از فایل که به صورت همزمان با بخشهای دیگر دانلود میشود، thread هستند.
پس میتوان اینگونه نتیجه گرفت:
- هر process میتواند چندین thread داشته باشد اما هر thread فقط میتواند برای یک process باشد.
- thread ها به صورت اشتراکی از منابع process استفاده میکنند.
مثال – برنامهنویسی با شنیدن موسیقی
فرض کنید همزمان با برنامهنویسی مشغول گوش دادن یک موزیک ملایم هستید تا تمرکز بهتری داشته باشید. شما برای این کار، دو برنامه (مثلا VS Code و VLC Player) را به صورت همزمان اجرا کردهاید.
اما در نگاه فنیتر این دو برنامه لزوما به صورت همزمان اجرا نمیشوند. دو پردازش ساخته میشود. یکی برای اجرای VS Code و دیگری برای اجرای VLC Player است. با توجه به اینکه هر پردازشگری در یک لحظه فقط میتواند یک دستور را پردازش کند، پردازشگر با سرعت بین process ها جابهجا شده و این باعث میشود که ما تصور کنیم این دو برنامه همزمان اجرا میشوند.

اما اگر کامپیوتر شما چندپردازنده داشته باشد این امکان وجود دارد که سیستمعامل، پردازش مربوط به هر یک را به صورت جداگانه روی یک پردازشگر انجام دهد. در این صورت واقعا برنامهها موازی اجرا میشوند. اما همه چیز به سیستمعامل بستگی دارد.
احتمال اینکه حتی در کامپیوترهای چندپردازندهای نیز اجرای دو کار مانند حالت قبل در یک پردازنده انجام شود نیز وجود دارد. زیرا به جز این دو برنامه، تسکهای دیگری نیز در حال اجرا هستند. این سیستمعامل است که تصمیم نهایی را میگیرد که کدام برنامه ها روی کدام پردازشگر اجرا شوند. شاید پردازندههای دیگر توسط پردازش های دیگر به قدری پر باشند که سیستمعامل تشخیص دهد باید این دو را روی یک پردازشگر اجرا کند.

از این مثال متوجه شدیم که:
- هر پردازنده میتواند چند process داشته باشد اما در هر لحظه فقط یکی از آنها را میتواند اجرا کند.
- هر process میتواند چند thread داشته باشد اما در هر لحظه فقط یکی از آنها پردازش میشود.
- سیستمعامل تصمیم نهایی را میگیرد که منابع سیستم چگونه اختصاص یابد.
ما با استفاده از ماژولهای Concurrent Programming میتوانیم به سیستمعامل پیشنهاد دهیم که چطور از منابع سیستم استفاده کند. (باز در نهایت تصمیم با اوست)

خلاصه
- هر کامپیوتر میتواند یک یا چند CPU داشته باشد.
- هر CPU میتواند یک یا چند هسته پردازنده داشته باشد.
- هر هسته پردازنده فقط و فقط میتواند یک task را در یک لحظه پردازش کند.
- هر پردازش میتواند یک یا چند thread داشته باشد.
- پردازشها را میتوان به صورت موازی اجرا کرد. (اگر CPU چند هسته ای باشد)
- thread ها را نمیتوان به صورت موازی اجرا کرد. (از نسخه ۳.۱۳ به بعد در پایتون میتوان thread ها را نیز موازی اجرا کرد.)
- فقط به تعداد هستههای سخت افزاری پردازنده میتوان پردازشهای موازی داشت.
- Thread های یک Process در یک لحظه میتوانند دسترسی مشترکی به Stack و Register و RAM داشته باشند.