مثال على Python Multiprocessing

في الدرس السابق، تعلمنا عن مثال Python CSV. في هذا الدرس، سنتعلم حول مضاعفة العمليات في Python مع أمثلة.

مضاعفة العمليات في Python

المعالجة المتوازية تحظى بمزيد من الاهتمام في الوقت الحالي. إذا كنت لا تزال لا تعرف عن المعالجة المتوازية، تعلم من ويكيبيديا. مع بدء مصنعو وحدات المعالجة المركزية في إضافة المزيد والمزيد من الأنوية إلى معالجاتهم، فإن إنشاء رمز متوازي هو وسيلة رائعة لتحسين الأداء. قدم Python وحدة multiprocessing لتتيح لنا كتابة رمز متوازي. لفهم الدافع الرئيسي لهذه الوحدة، علينا أن نعرف بعض الأساسيات حول البرمجة المتوازية. بعد قراءة هذا المقال، نأمل أن تكون قادرًا على جمع بعض المعرفة حول هذا الموضوع.

مضاعفة العمليات في Python: العملية، الطابور، والأقفال

هناك العديد من الفئات في وحدة multiprocessing في Python لبناء برنامج متوازي. من بينها، ثلاث فئات أساسية هي Process، Queue و Lock. هذه الفئات ستساعدك في بناء برنامج متوازي. ولكن قبل الحديث عنها، دعنا نبدأ هذا الموضوع بكود بسيط. لجعل برنامج متوازي مفيدًا، يجب عليك معرفة كم من النوى موجود في جهاز الكمبيوتر الخاص بك. تمكن وحدة multiprocessing في Python من معرفة ذلك. الكود البسيط التالي سيطبع عدد النوى في جهاز الكمبيوتر الخاص بك.

import multiprocessing

print("Number of cpu : ", multiprocessing.cpu_count())

الناتج التالي قد يختلف على جهاز الكمبيوتر الخاص بك. بالنسبة لي، عدد النوى هو 8.

فئة Process في multiprocessing بلغة Python

فئة العمليات في تعداد عمليات Python هي تجريد يقوم بإعداد عملية Python أخرى، وتوفيرها لتشغيل الكود وطريقة لتحكم التطبيق الأصلي في التنفيذ. هناك وظيفتان مهمتان تنتميان إلى فئة العمليات – وظيفة start() ووظيفة join(). في البداية، نحتاج إلى كتابة وظيفة، التي ستُشغَّلها العملية. ثم، نحتاج إلى إنشاء كائن عملية. إذا قمنا بإنشاء كائن عملية، لن يحدث شيء حتى نخبره ببدء المعالجة عبر وظيفة start(). بعد ذلك، ستقوم العملية بالتشغيل وإرجاع نتيجتها. بعد ذلك نخبر العملية بالاكتمال من خلال وظيفة join(). بدون استدعاء وظيفة join()، ستظل العملية غير نشطة ولن تتوقف. لذلك، إذا قمت بإنشاء العديد من العمليات ولم تُنهها، فقد تواجه ندرة في الموارد. ثم قد تحتاج إلى إنهاءها يدويًا. شيء مهم آخر هو، إذا كنت ترغب في تمرير أي وسيط من خلال العملية، فيجب عليك استخدام وسيط الكلمة المفتاحية args. الشيفرة التالية ستكون مفيدة لفهم استخدام فئة العمليات.

from multiprocessing import Process


def print_func(continent='Asia'):
    print('The name of continent is : ', continent)

if __name__ == "__main__":  # confirms that the code is under main function
    names = ['America', 'Europe', 'Africa']
    procs = []
    proc = Process(target=print_func)  # instantiating without any argument
    procs.append(proc)
    proc.start()

    # تحديد عملية مع الوسائط
    for name in names:
        # print(name)
        proc = Process(target=print_func, args=(name,))
        procs.append(proc)
        proc.start()

    # إكمال العمليات
    for proc in procs:
        proc.join()

سيكون مخرج الشيفرة التالية:

فئة قائمة انتظار تعداد العمليات

لديك معرفة أساسية حول هياكل بيانات الكمبيوتر، ربما تعرف عن طابور الانتظار. توفر وحدات معالجة Python Multiprocessing فئة Queue وهي هيكل بيانات الدخول الأول، الخروج الأول تخزن أي كائن Python يمكن تخزينه (على الرغم من أن الأشكال البسيطة هي الأفضل) وهي مفيدة للغاية لمشاركة البيانات بين العمليات. تكون الطوابور مفيدة بشكل خاص عند تمريرها كمعلمة إلى دالة الهدف للعملية لتمكين العملية من استهلاك البيانات. يمكننا إدراج البيانات في الطابور باستخدام الدالة put() واستخدام الدالة get() للحصول على العناصر من الطوابور. انظر إلى الشفرة التالية لمثال سريع.

from multiprocessing import Queue

colors = ['red', 'green', 'blue', 'black']
cnt = 1
# تعريف كائن طابور
queue = Queue()
print('pushing items to queue:')
for color in colors:
    print('item no: ', cnt, ' ', color)
    queue.put(color)
    cnt += 1

print('\npopping items from queue:')
cnt = 0
while not queue.empty():
    print('item no: ', cnt, ' ', queue.get())
    cnt += 1

فئة قفل Python multiprocessing

مهمة فئة القفل بسيطة جدًا. يسمح بالحصول على القفل بحيث لا يمكن لعملية أخرى تنفيذ الكود المماثل حتى يتم إطلاق القفل. لذا، مهمة فئة القفل تتمثل أساسًا في شيئين. أحدهما هو الحصول على القفل والآخر هو إطلاق القفل. للحصول على القفل، يتم استخدام الدالة acquire() ولإطلاق القفل يتم استخدام الدالة release().

مثال Python multiprocessing

في هذا المثال عن التعددية في Python، سندمج كل معرفتنا معًا. لنفترض أن لدينا بعض المهام التي يجب القيام بها. لإنجاز تلك المهمة، سنستخدم عدة عمليات. لذلك، سنحتفظ بصفيفين. الأول سيحتوي على المهام والآخر سيحتوي على سجل المهام المكتملة. ثم نقوم بإنشاء العمليات لإكمال المهمة. يجدر بالذكر أن فئة Queue في Python متزامنة بالفعل. وهذا يعني أننا لا نحتاج إلى استخدام فئة Lock لمنع عمليات متعددة من الوصول إلى نفس كائن الصف. لهذا السبب، لا نحتاج إلى استخدام فئة Lock في هذه الحالة. فيما يلي التنفيذ حيث نقوم بإضافة المهام إلى الصف، ثم إنشاء العمليات وبدء تشغيلها، ثم استخدام join() لإكمال العمليات. وأخيرًا، نقوم بطباعة السجل من الصف الثاني.

from multiprocessing import Lock, Process, Queue, current_process
import time
import queue # imported for using queue.Empty exception


def do_job(tasks_to_accomplish, tasks_that_are_done):
    while True:
        try:
            '''
                try to get task from the queue. get_nowait() function will 
                raise queue.Empty exception if the queue is empty. 
                queue(False) function would do the same task also.
            '''
            task = tasks_to_accomplish.get_nowait()
        except queue.Empty:

            break
        else:
            '''
                if no exception has been raised, add the task completion 
                message to task_that_are_done queue
            '''
            print(task)
            tasks_that_are_done.put(task + ' is done by ' + current_process().name)
            time.sleep(.5)
    return True


def main():
    number_of_task = 10
    number_of_processes = 4
    tasks_to_accomplish = Queue()
    tasks_that_are_done = Queue()
    processes = []

    for i in range(number_of_task):
        tasks_to_accomplish.put("Task no " + str(i))

    # إنشاء العمليات
    for w in range(number_of_processes):
        p = Process(target=do_job, args=(tasks_to_accomplish, tasks_that_are_done))
        processes.append(p)
        p.start()

    # إكمال العمليات
    for p in processes:
        p.join()

    # طباعة النتيجة
    while not tasks_that_are_done.empty():
        print(tasks_that_are_done.get())

    return True


if __name__ == '__main__':
    main()

اعتمادًا على عدد المهام، سيستغرق الكود بعض الوقت لإظهار النتيجة. ستتفاوت النتيجة من وقت لآخر.

تعددية Python في استخدام Pool

يمكن استخدام Pool في التعددية في Python لتنفيذ وظيفة عبر قيم مدخلة متعددة، وتوزيع البيانات المدخلة عبر العمليات (تعددية البيانات). فيما يلي مثال بسيط عن Pool في تعددية Python.

from multiprocessing import Pool

import time

work = (["A", 5], ["B", 2], ["C", 1], ["D", 3])


def work_log(work_data):
    print(" Process %s waiting %s seconds" % (work_data[0], work_data[1]))
    time.sleep(int(work_data[1]))
    print(" Process %s Finished." % work_data[0])


def pool_handler():
    p = Pool(2)
    p.map(work_log, work)


if __name__ == '__main__':
    pool_handler()

الصورة أدناه تُظهر نتيجة البرنامج أعلاه. لاحظ أن حجم المجموعة هو 2، لذا يحدث تنفيذان متوازيان لوظيفة work_log. عند انتهاء أحد عمليات المعالجة، يتم اختيار الوسيط التالي وهكذا. هكذا، هذا كل شيء بالنسبة لوحدة المعالجة المتعددة في Python. المرجع: الوثائق الرسمية

Source:
https://www.digitalocean.com/community/tutorials/python-multiprocessing-example