كيفية تنفيذ التوازي باستخدام مكتبة multiprocessing في Python

تمت الكتابة بواسطة: عبد الحكيم

تارخ آخر تحديث: 19 ديسمبر 2024

محتوى المقال

كيفية تنفيذ التوازي باستخدام مكتبة multiprocessing في Python

في العديد من التطبيقات البرمجية، قد نحتاج إلى تنفيذ مهام متعددة في وقت واحد لتحسين الأداء واستغلال موارد المعالجة بشكل أكثر فعالية. مكتبة multiprocessing في Python تتيح لك تحقيق هذا الهدف من خلال السماح بإنشاء وإدارة عمليات متعددة تعمل بشكل متوازٍ. في هذا المقال، سنتعلم كيفية استخدام multiprocessing لتنفيذ التوازي في برامج Python.

ما هي مكتبة multiprocessing؟

multiprocessing هي مكتبة قياسية في Python تُستخدم لإنشاء وإدارة عمليات متعددة (processes). على عكس threading التي تتيح إنشاء سلاسل عمليات (threads)، فإن multiprocessing ينشئ عمليات مستقلة تعمل في ذاكرة منفصلة، مما يعني أنه يمكنها الاستفادة الكاملة من أنوية المعالج المتعددة.

لماذا نستخدم multiprocessing؟

في بعض الأحيان، يكون هناك حاجة لتنفيذ مهام ثقيلة حسابيًا والتي قد تستفيد من تشغيلها على أكثر من نواة واحدة من المعالج. في Python، بسبب قيود GIL (Global Interpreter Lock)، فإن استخدام الخيوط (threads) لا يكون فعالاً في تنفيذ التوازي الحقيقي للمهام. هنا يأتي دور multiprocessing، حيث يمكنه تنفيذ عمليات متوازية حقيقية عبر أنوية المعالج المختلفة.

إنشاء عملية جديدة

أسهل طريقة لإنشاء عملية جديدة هي استخدام الفئة Process. سنقوم بإنشاء عملية بسيطة تنفذ دالة معينة:

مثال على إنشاء عملية

لنقم بإنشاء عملية تقوم بطباعة رسالة معينة:

from multiprocessing import Process

def print_message():
    print("مرحبًا من العملية!")

# إنشاء العملية
p = Process(target=print_message)

# بدء العملية
p.start()

# انتظار اكتمال العملية
p.join()

في هذا المثال، قمنا بإنشاء عملية جديدة باستخدام الفئة Process، ومررنا إليها الدالة print_message لتعمل عند بدء العملية. بعد بدء العملية باستخدام start()، ننتظر انتهاء العملية باستخدام join().

تمرير المعاملات إلى العمليات

يمكنك تمرير المعاملات إلى العملية عن طريق تمريرها إلى الدالة المستهدفة (target function) عند إنشاء العملية. لنفترض أن لدينا دالة تأخذ معاملًا وتطبعه:

def print_square(number):
    print(f"مربع {number} هو {number**2}")

# إنشاء عملية جديدة مع تمرير المعامل
p = Process(target=print_square, args=(5,))

# بدء العملية
p.start()
p.join()

في هذا المثال، نقوم بتمرير الرقم 5 إلى الدالة print_square عند إنشاء العملية. يتم استخدام المعامل args لتمرير المعاملات على شكل tuple.

مشاركة البيانات بين العمليات

بما أن كل عملية تعمل في مساحة ذاكرة منفصلة، فإن مشاركة البيانات بين العمليات تتطلب استخدام أدوات معينة مثل Queue، Pipe، أو استخدام كائنات مشاركة مثل Value وArray.

مشاركة البيانات باستخدام Queue

Queue هي واحدة من أسهل الطرق لمشاركة البيانات بين العمليات. يمكنك استخدام put() لإضافة بيانات إلى الطابور وget() لاستخراج البيانات.

from multiprocessing import Process, Queue

def square_numbers(numbers, queue):
    for n in numbers:
        queue.put(n**2)

# إنشاء الطابور
q = Queue()

# إنشاء العملية
p = Process(target=square_numbers, args=([1, 2, 3, 4], q))

# بدء العملية
p.start()
p.join()

# استخراج البيانات من الطابور
while not q.empty():
    print(q.get())

في هذا المثال، نستخدم Queue لمشاركة نتائج تربيع الأرقام بين العملية الرئيسية والعملية الفرعية. تقوم العملية الفرعية بوضع النتائج في الطابور، بينما تقوم العملية الرئيسية باستخراج النتائج من الطابور بعد انتهاء العملية الفرعية.

مشاركة البيانات باستخدام Value و Array

إذا كنت تحتاج إلى مشاركة بيانات بسيطة مثل الأعداد أو المصفوفات، يمكنك استخدام Value وArray اللذان يتيحان لك مشاركة هذه البيانات بين العمليات.

from multiprocessing import Process, Value, Array

def modify_data(num, arr):
    num.value += 10
    for i in range(len(arr)):
        arr[i] *= 2

# إنشاء كائن Value و Array
shared_num = Value('i', 5) # 'i' تعني عدد صحيح
shared_arr = Array('i', [1, 2, 3, 4])

# إنشاء العملية
p = Process(target=modify_data, args=(shared_num, shared_arr))

# بدء العملية
p.start()
p.join()

# عرض القيم المشتركة
print("القيمة المشتركة:", shared_num.value)
print("المصفوفة المشتركة:", shared_arr[:])

في هذا المثال، قمنا بإنشاء كائن Value يحتوي على عدد صحيح وكائن Array يحتوي على مصفوفة من الأعداد الصحيحة. ثم قمنا بتمرير هذه الكائنات إلى العملية التي تعدلها. بعد انتهاء العملية، يمكننا عرض القيم المعدلة.

التعامل مع التجمعات (Pools)

إذا كنت تحتاج إلى تنفيذ نفس العملية على مجموعة كبيرة من البيانات، يمكن أن يكون استخدام Pool أكثر فعالية. تتيح لك Pool توزيع المهام على عدة عمليات بشكل أوتوماتيكي.

مثال على استخدام Pool

لنقم بإنشاء Pool من العمليات لمعالجة قائمة من الأرقام وحساب مربعاتها:

from multiprocessing import Pool

def square(n):
    return n**2

# إنشاء التجمع
with Pool() as p:
    results = p.map(square, [1, 2, 3, 4, 5])

print(results)

في هذا المثال، قمنا بإنشاء Pool من العمليات واستخدمنا map لتوزيع عملية حساب مربعات الأرقام على جميع العمليات المتاحة في التجمع.

الخاتمة

تعد مكتبة multiprocessing أداة قوية في Python لتنفيذ التوازي الحقيقي واستغلال قدرات المعالجات متعددة الأنوية. من خلال إنشاء عمليات مستقلة، مشاركة البيانات بين هذه العمليات، واستخدام التجمعات (Pools)، يمكنك بناء برامج أكثر كفاءة وفعالية في معالجة المهام الثقيلة. نأمل أن يكون هذا المقال قد قدم لك فهماً جيداً لكيفية استخدام multiprocessing في Python.

طور مهاراتك: مقالات يجب قراءتها في البرمجة