فهم تعدد المهام Concurrency يتطلب استيعاب لبعض المفاهيم الأساسية، وصعوبة كتابة الكود تأتي من عدم وضوح الهدف المراد تحقيقه، فتعدد المهام ليس أداة واحدة، بل مجموعة من التقنيات، واختيار التقنية المناسبة يعتمد على ما تريد تحقيقه.
أولاً الـ Processes أو العمليات هي عملية مستقلة تمامًا عن باقي العمليات، لها مساحة ذاكرة خاصة بها، وإنشاء عملية جديدة يعني إنشاء برنامج جديد تمامًا.
وذلك مناسب للمهام التي تتطلب عزلًا تامًا أو استخدام موارد كبيرة، والمكتبة المستخدمة هي multiprocessing.
للتوضيح بمثال بسيط لحساب مربع الأعداد من 1 إلى 5 في عمليات منفصلة:
import multiprocessing
def square(n):
return n * n
if __name__ == '__main__':
with multiprocessing.Pool(processes=5) as pool:
results = pool.map(square, range(1, 6))
print(results) # Output: [1, 4, 9, 16, 25]
المفهوم الثاني هو الخيوط Threads وهي خيوط متعددة تعمل ضمن نفس العملية، وتشارك نفس مساحة الذاكرة، وإنشاء خيط أسرع من إنشاء عملية، لكنها تشارك الموارد.
وهي مناسبة للمهام التي تتطلب سرعة عالية وتشارك البيانات، لكن يجب الانتباه إلى مشاكل التزامن (Race Conditions).
والمكتبة المستخدمة هي threading.
وإليك مثال بسيط لطباعة رسائل من خيوط مختلفة:
import threading
import time
def print_message(message):
for i in range(5):
print(f"Thread {threading.current_thread().name}: {message}")
time.sleep(1)
if __name__ == '__main__':
thread1 = threading.Thread(target=print_message, args=("Hello",))
thread2 = threading.Thread(target=print_message, args=("World",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
المفهوم الثالث هو المهام المتزامنة Coroutine/Asyncio ولا تنشئ عمليات أو خيوط جديدة، بل تنتقل بين المهام بشكل غير متزامن باستخدام الكلمات المفتاحية async و await، ومناسب للمهام التي تتضمن انتظارًا (مثل طلبات الشبكة) أي عمليات I/O كثيفة (مثل الشبكات، قواعد البيانات)، حيث لا يتم حجب الخيط أثناء الانتظار.
والمكتبة المستخدمة هي asyncio، وكمثال بسيط لطباعة رسائل مع تأخير باستخدام Asyncio:
import asyncio
async def print_message(message, delay):
await asyncio.sleep(delay)
print(f"Message: {message}")
async def main():
await asyncio.gather(
print_message("Hello", 2),
print_message("World", 1)
)
asyncio.run(main())
بالتالي عليك تحديد ما الذي تريد تحقيقه بتعدد المهام؟ هل تريد تسريع عملية حسابية؟ هل تريد التعامل مع طلبات متعددة من الشبكة؟
فالعمليات مناسبة للمهام المستقلة، الخيوط للمهام التي تشارك البيانات، والمهام المتزامنة للمهام التي تتضمن انتظارًا.
ولا تحاول كتابة برنامج معقد من البداية، ابدأ بمثال بسيط وفهم كيفية عمله قبل إضافة تعقيدات.