المقدمة

الـ Concurrent Programming مبنية على فكرة إن يكون عندي الـ Application متكسر لـ Independent Tasks يعني مهام مستقلة أو Units اقدر اشتعل عليهم بشكل Concurrently.

وطبعًا الـ Abstraction Layer واللي بتسهلنا أغلب الشغل ده ألا وهو الـ OS قادر إنه يدير الـ Tasks دي ويعملهم Handling بشكل كويس من خلال إنه يوفر الـ Resources اللازمة بشكل كفء.

فنقدر نعتبر الـ Concurrent Programming هي مجموعة من الـ Abstractions اللي بتسمح للمطورين إنهم ينظموا التطبيق بتاعهم على شكل مهام صغيرة ومستقلة وينقلوها بعد كده للـ Runtime System واللي في الحالة دي هو الـ OS عشان يعملهم Handling بكفاءة.

وطبعا الـ Runtime System هيكون المسئول عن تنفيذهم وعملية الـ Orchestration اللي هتتم بينهم عشان يختار انهي Task اللي هيتم تنفيذها ويستغل الـ Resources بأفضل شكل ممكن.

وخلاصة الـ Abstractions اللي عمالين نتكلم عليها دي هي إن احنا عندنا بكل بساطة نوعين من الـ Abstractions كمطورين عشان نتعامل مع الـ Concurrent Programming والنوعين دول هم الـ Building Blocks اللي ممكن نعتمد عليهم عامة في تطبيق الـ Concurrency ألا وهم الـ Process والـ Threads.


اتكلمنا المرة اللي فاتت عن الـ Process وشوفنا مع بعض شوية من الـ Internals بتاعتها وقد ايه موضوع إنشاء أو حذف Process هي عملية مكلفة وشوفنا ازاي هي بتتميز بالاستقلالية والعزل.

Concurrency Building Blocks (Process)
الـ Concurrent Programming مبنية على فكرة إن يكون عندي الـ Application متكسر لـ Independent Tasks يعني مهام مستقلة أو Units اقدر اشتعل عليهم بشكل Concurrently.

Concurrency Building Blocks (Process)

المرة دي هنتكلم عن تاني Building Block في الـ Concurrent Programming ألا وهي الـ Threads.


الـ Threads

مشاركة الـ memory بين الـ processes وبعضها ممكنة في معظم الـ OSs، بس محتاجة مجهود إضافي مننا كمطورين للتعامل معاها. وعشان كده فيه abstraction تاني بيسمح لينا بمشاركة الـ memory بشكل فعال أكتر: وهو الـ threads.

في النهاية، البرنامج بتاعنا هو مجرد مجموعة من الأوامر اللي لازم تتنفذ واحدة ورا التانية بالترتيب. وعشان ده يحصل، الـ OS بيستخدم مفهوم الـ thread. من الناحية التقنية، الـ thread بتتعرف على إنها سيل مستقل من التعليمات اللي الـ OS ممكن يجدول تنفيذها.

فاكرين لما قولنا إن الـ process هو برنامج شغال بالإضافة لشوية موارد معمولها Allocation ليه؟

لو قسمنا البرنامج لمكونات منفصلة، الـ process بتكون هي الـ container للموارد دي زي (address space، ملفات، اتصالات، إلخ)، والـ thread هي الجزء الديناميكي— مجموعة من التعليمات بتتنفذ جوة الـ container.

Single Threaded Process vs Multi-Threaded Process

الـ threads اتولدت من فكرة إن أسهل طريقة لمشاركة البيانات بين الـ processes اللي بتتفاعل مع بعض، هي إنهم يشاركوا الـ (address space). وعشان كده، الـ threads اللي في نفس الـ process، نقدر نعتبرهم زي الـ processes اللي ممكن تشارك الموارد بسهولة مع بعض ومع الـ parent process بتاعتهم، وكذلك الـ address space، والملفات، والاتصالات، والبيانات المشتركة، وهكذا.

الـ threads كمان بتحافظ على الحالة بتاعتها عشان تسمح بالتنفيذ الآمن والمستقل للتعليمات بتاعتها. كل thread مش واخدة بالها من الـ threads التانية إلا لو كانت بتتداخل معاهم عن قصد.

فالـ OS بيدير الـ threads وممكن يوزعهم على الـ CPUs المتاحة. وعشان كده، تطوير برنامج multithreaded ممكن يكون طريقة سهلة لتشغيل كذا task في نفس الوقت بالنسبة لكتير من الناس.


مثال واقعي لتوضيح الفرق بين الـ Process والـ Thread

عشان نوضح الفرق بين الـ processes والـ threads، خلينا نشوف مثال بسيط. تخيل إننا عندنا شركة مقاولات، واحنا بنوظف 3 فرق بناء عشان يشتغلوا على 3 مشاريع مختلفة في نفس الوقت.

فهنعتبر كل فرقة بناء هي (process) مخصصة لمشروع واحد (مهمة) ليها الأدوات بتاعتها، وخطة المشروع، والموارد كذلك. ده حال الـ multi processes.

ولكن من ناحية تانية، عشان نقدر وفر فلوس، احنا ممكن نوظف فرقة بناء واحدة بس لكل الـ 3 مشاريع مع بعض، ونخليهم يستخدموا نفس الأدوات ونفس الموارد، بس هيكون فيه قايمة من التعليمات منفصلة على حسب كل مشروع، وده شبيه أكتر بالـ threads.

فهنقدر نلاقي أن سهل جدًا يتم التواصل والـ communication بين الـ threads وبعضهم وده لانهم تقريبًا بيتشاركوا كل حاجة داخل الـ process الواحدة ، فالفريق الواحد يقدر يستعمل نفس الادوار والموارد اللي موجودة ، بدلًا ما يبقى عندنا الكلام ده 3 مرات والتواصل مع الفرق وبعضها اكيد هيكون أصعب ومكلف.


مميزات الـ Threads

الـ threads ليها بعض المميزات والعيوب زيها زي أي حاجة فتعالوا نشوف مع بعض مميزات وعيوب الـ threads:

استهلاك أقل للذاكرة

الـ processes زي ما شوفنا بتتميز باستقلالها تماما وكونها منعزلة، وكل واحدة ليها الـ address space الخاصة بيها، ومجموعة من الـ threads، وبعض النسخ من المتغيرات المستقلة تماما عن نفس المتغيرات في الـ processes التانية.

الـ threads استهلاكها للذاكرة أقل بكتير من الـ fork() function العادية، عشان الـ parent thread مش بتتنسخ— الـ threads بتستخدم نفس الـ process. وعشان كده، الـ threads أحيانا بيتسموا lightweight processes.

نتيجة لكده، ممكن نعمل عدد threads أكتر من الـ processes على نفس النظام. فإنشاء وإنهاء الـ threads أسرع من الـ processes، لانها بتاخد وقت أقل من الـ OS عشان يخصص ويدير الـ resources بتاعة الـ thread.

وبالتالي ممكن نعمل threads في أي وقت يكون مناسب في التطبيق بتاعنا بحرية كاملة، ومنقلقش إننا بنضيع وقت الـ CPU والـ memory مقارنة بالخوف اثناء من التعامل مع الـ multi processes.


تواصل أسرع

كل process بتشتغل مع الـ memory الخاصة بتاعتها. وزي ما وضحنا قبل كده الـ processes مينفعش يتبادلوا حاجة إلا عن طريق آلية تواصل بين الـ processes، وبعضها وفيه أكتر من طريقة لتحقيق ده.

الـ threads بتستخدم نفس الـ address space اللي موجود في الـ process، وبالتالي ممكن يتواصلوا مع بعض عن طريق القراءة والكتابة في الـ address space المشتركة بتاعة الـ parent process من غير أي مشاكل: أي حاجة بيغيرها thread واحد بتكون متاحة على طول للباقي وبشكل لحظي.


الاحتياج لعملية التزامن

الـ OS بيوفر استقلالية كاملة للـ processes عن بعض، عشان لو واحدة منهم حصلها crash، الـ processes التانية متتأذيش. ولكن للأسف ده مش بيحصل مع الـ threads: عشان كل الـ threads في process واحدة بيستخدموا نفس الموارد المشتركة، فلو واحدة حصلها crash أو باظت، الباقي غالبا هيتأثر نتيجة لده.

وعشان نمنع ده من انه يحصل، احنا كمبرمجين لازم نخلي بالنا من موضوع المزامنة بين الـ threads وبعضها أو اللي بنقول عليه (synchronization) بين الموارد المشتركة بينهم، ويكون عندنا تحكم أكتر في سلوك الـ threads.


في الختام

بكده نكون اتكلمنا عن تاني Building Block وهي الـ Threads وشوفنا مع بعض الفرق بينها وبين الـ Processes , وشوفنا مميزاتها والعيوب أو التحديات اللي محتاجين ناخد بالنا منها واحنا بنتعامل معاها ونقدر بكده نختصر الكلام ده ان أي برنامج شغال هو Process ممكن يكون ليها