البرمجة المتوازية في Python: متى تستخدم Threading ومتى تختار Multiprocessing?

5 دقائق للقراءة

في عالم البرمجة الحديث، السرعة والكفاءة هما مفتاح النجاح. تخيل أنك تعمل على تطبيق يحتاج لمعالجة آلاف الصور، أو تحميل بيانات من عشرات المواقع، أو إجراء حسابات معقدة على كميات ضخمة من البيانات. إذا قمت بتنفيذ هذه المهام واحدة تلو الأخرى، قد يستغرق الأمر ساعات!
كما أن تجربة المستخدم وثقته في التطبيق، تتأثر بشكل واضح بأداء التطبيق واستخدامه للموارد، بغض النظر عن تعقيد المهام التي يقوم بها.
هنا يأتي دور البرمجة المتوازية، التي تسمح لبرنامجك بتنفيذ عدة مهام في نفس الوقت، مما يوفر الوقت ويحسن الأداء بشكل كبير. في Python، لديك أداتان قويتان لتحقيق ذلك: Multi-threading و Multiprocessing.
لكن السؤال المهم: متى تستخدم كل واحدة منهما؟ وما الفرق الحقيقي بينهما؟

ما هو Multi-threading?

الشائع والمتداول أن Multi-threading يقوم بتوزيع المهام على أنوية مختلفة من المعالج (Processor) ويتم تنفيذ تلك المهام بشكل متوازي في نفس الوقت، ولكن هذا ليس صحيح تماما.
تخيل أن لديك طباخ واحد في المطبخ يقوم بتحضير عدة أطباق. هذا الطباخ يبدأ بطهي الأرز، وبينما الأرز يُطهى، يذهب لتقطيع الخضروات، ثم يعود لتقليب الأرز، وهكذا. هو شخص واحد لكنه يتنقل بين المهام.
Threading يعمل بنفس الطريقة: برنامج واحد ينفذ عدة مهام بالتبادل بينها بسرعة كبيرة حتى تبدو وكأنها تعمل معاً في نفس الوقت.

مثال تقني:

تخيل أنك تقوم بتطوير تطبيق وظيفته تنزيل فيديوهات من الإنترنت وتستخدم multi threading في عملية التنزيل، هنا الthreads لا تعني تنزيل الفيديوهات معاً في نفس الوقت، ولكن يقوم البرنامج بإرسال طلب (Request)لتحميل فيديو، وأثناء انتظاره الرد من الشبكة (Response) يرسل طلب لتحميل فيديو آخر.
أي أن كل ما يحدث هو استغلال وقت الانتظار في تنفيذ شيء آخر، مما يحسن من سرعة وأداء التطبيق.
ولكن لماذا لا يوجد توازي حقيقي في الmulti threading، هذا ما سيتم إجابته في المفهوم التالي.

ما هو GIL – القفل العالمي للمُفسر:

Python كونها لغة مفسر (Interpreter) لا يمكنها تنفيذ أكثر من كود في نفس الوقت.
لذلك يأتي هنا GIL وهو اختصار لـ Global Interpreter Lock، وهو مثل “مفتاح واحد” في Python يسمح لـ thread واحد فقط بتنفيذ كود Python في نفس الوقت.
تخيل أن لديك 10 موظفين في مكتب، لكن هناك جهاز كمبيوتر واحد فقط يمكن استخدامه. حتى لو كان لديك 10 موظفين، واحد فقط يمكنه العمل في أي لحظة.
وبالتالي بسبب GIL، لا يمكن تشغيل أكثر من thread واحد في نفس اللحظة، حتى لو كان لديك معالج متعدد الأنوية.
ولكن كيف يمكننا تخطي GIL واستخدام توازي حقيقي؟ هذا ما سنكتشفه من خلال Multiprocessing.

ما هو Multiprocessing؟

الآن تخيل أن لديك طباخان في المطبخ، كل واحد يعمل بشكل مستقل تماماً. الأول يطهي الأرز والثاني يحضر السلطة، كل منهما لديه أدواته الخاصة ومساحته الخاصة.
Multiprocessing يعمل بنفس الطريقة: يتم إنشاء عمليات منفصلة تعمل بشكل متوازي حقيقي.
أي أن كل عملية يكون لها مفسر بايثون مستقل وGIL منفصل عن باقي العمليات.

مثال تقني:

في مثالنا عن تطبيق تنزيل الفيديوهات, إذا لدينا 10 مهام لتنزيل 10 فيديوهات، هنا كل عملية تشغل نسخة مستقلة من مفسر بايثون وتنفذ عليه عملية التنزيل كاملة، وبالتالي يتم تنزيل الفيديوهات معاً في نفس الوقت وليس فقط استغلال وقت الانتظار.

الذاكرة والموارد:

هناك اختلاف في الطريقة التي يتم بها مشاركة البيانات بين المفهومين، وهي الأساس الحاسم في اختيار أحدهم.

Threading – ذاكرة مشتركة:

في Threading، جميع الـ threads تتشارك في نفس الذاكرة، مثل عائلة تعيش في منزل واحد وتستخدم نفس الأثاث.

مثال تقني:

في مثال تطبيق تحميل الفيديوهات الthreads ستقرأ روابط الفيديوهات من قائمة مشتركة، وبالتالي استهلاك أقل للذاكرة.

Multiprocessing – ذاكرة منفصلة:

في Multiprocessing، كل process لها ذاكرة منفصلة، مثل عائلات تعيش في منازل منفصلة.

مثال تقني:

هنا مهام التنزيل ستأخذ نسخة خاصة بها (copy) من قائمة الروابط، وهذا ما يترتب عليه التالي:

  • استخدام أكثر للموارد.
  • عزل كامل بين العمليات.
  • التواصل بين العمليات يحتاج آليات خاصة.

حالات الاستخدام المثالية:

قد نتوقع بسبب تحقيق التوازي الحقيقي في الmulti processing أنه الخيار المثالي في كل الحالات، ولكن هذا ليس صحيح تماما، هناك حالات (Best practice) لكل من هما:

استخدم Threading عندما:

  • المهام المرتبطة بالإدخال/الإخراج (I/O-bound)
  • عندما يقضي برنامجك معظم وقته في الانتظار، مثل:
    • قراءة أو كتابة الملفات
    • طلبات الشبكة (HTTP requests)
    • الاتصال بقواعد البيانات
    • انتظار إدخال المستخدم

استخدم Multiprocessing عندما:

  • المهام المرتبطة بالمعالج (CPU-bound)
  • عندما يقضي برنامجك معظم وقته في الحسابات، مثل:
    • المعالجة الرياضية المعقدة
    • معالجة الصور والفيديو
    • تشفير البيانات
    • التعلم الآلي والذكاء الاصطناعي

الخلاصة:

يُعد تحسين أداء التطبيق ركن أساسي يجب الاهتمام به، بما يمتلكه من تأثير قوي على تجربة المستخدم، ولكن لا يوجد خيار أفضل بشكل مطلق.
الاختيار الصحيح يعتمد على نوع المهمة وليس على اختيار ما يحقق توازي حقيقي، بل في حالات كثيرة، البرمجة المتوازية ليست دائماً الحل. أحياناً، تحسين الخوارزمية نفسها يعطيك تحسناً أكبر بكثير من استخدام Threading أو Multiprocessing، ابحث دائماً عن الحل الأبسط والأكثر فعالية.

عن MahmoudAtef

تحقق أيضا

حول Python Install Manager على Windows.

6 دقائق للقراءةلغة البرمجة Python غنية عن التعريف, وستجد معظم الشركات الكبيرة تستخدمها في مهام مختلفة. تعد… أكمل القراءة » حول Python Install Manager على Windows.

اكتب تعليقًا