Shembull i shumëpërpunimit të Python


Në tutorialin tonë të mëparshëm, mësuam rreth Shembullit Python CSV. Në këtë tutorial do të mësojmë Python Multiprocessing me shembuj.

Python Multiprocessing

Përpunimi paralel po merr më shumë vëmendje në ditët e sotme. Nëse ende nuk dini për përpunimin paralel, mësoni nga wikipedia. Ndërsa prodhuesit e CPU-së fillojnë të shtojnë gjithnjë e më shumë bërthama në procesorët e tyre, krijimi i kodit paralel është një mënyrë e shkëlqyer për të përmirësuar performancën. Python prezantoi modulin multiprocessing për të na lejuar të shkruajmë kodin paralel. Për të kuptuar motivimin kryesor të këtij moduli, duhet të dimë disa baza për programimin paralel. Pas leximit të këtij artikulli, shpresojmë se do të jeni në gjendje të mblidhni disa njohuri mbi këtë temë.

Procesi i shumëpërpunimit të Python, radhët dhe kyçet

Ka shumë klasa në modulin e shumëpërpunimit python për ndërtimin e një programi paralel. Midis tyre, tre klasa bazë janë Procesi, Radha dhe Bllokimi. Këto klasa do t'ju ndihmojnë të ndërtoni një program paralel. Por përpara se të përshkruajmë rreth tyre, le ta inicojmë këtë temë me kod të thjeshtë. Për ta bërë të dobishëm një program paralel, duhet të dini sa bërthama ka në kompjuterin tuaj. Moduli Python Multiprocessing ju mundëson ta dini këtë. Kodi i thjeshtë i mëposhtëm do të printojë numrin e bërthamave në kompjuterin tuaj.

import multiprocessing

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

Klasa e procesit të shumëpërpunimit të Python

Klasa Proces e shumëpërpunimit të Python është një abstraksion që konfiguron një proces tjetër Python, i siguron atij për të ekzekutuar kodin dhe një mënyrë për aplikacionin prind për të kontrolluar ekzekutimin. Ekzistojnë dy funksione të rëndësishme që i përkasin klasës Process - funksioni start() dhe join(). Në fillim, ne duhet të shkruajmë një funksion, i cili do të drejtohet nga procesi. Pastaj, ne duhet të instantojmë një objekt procesi. Nëse krijojmë një objekt procesi, asgjë nuk do të ndodhë derisa t'i themi të fillojë përpunimin përmes funksionit start(). Më pas, procesi do të ekzekutohet dhe do të kthejë rezultatin e tij. Pas kësaj, ne themi që procesi të përfundojë përmes funksionit join(). Pa thirrjen e funksionit join(), procesi do të mbetet i papunë dhe nuk do të përfundojë. Pra, nëse krijoni shumë procese dhe nuk i përfundoni ato, mund të përballeni me mungesë burimesh. Atëherë mund t'ju duhet t'i vrisni me dorë. Një gjë e rëndësishme është, nëse doni të kaloni ndonjë argument përmes procesit, duhet të përdorni argumentin e fjalës kyçe args. Kodi i mëposhtëm do të jetë i dobishëm për të kuptuar përdorimin e klasës Process.

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()

    # instantiating process with arguments
    for name in names:
        # print(name)
        proc = Process(target=print_func, args=(name,))
        procs.append(proc)
        proc.start()

    # complete the processes
    for proc in procs:
        proc.join()

Python multiprocessing class Queue

Ju keni njohuri bazë për strukturën e të dhënave kompjuterike, ju ndoshta dini për Queue. Modulet Python Multiprocessing ofrojnë klasën Queue që është saktësisht një strukturë e të dhënave First-In-First-Out. Ata mund të ruajnë çdo objekt Python turshi (megjithëse ato të thjeshta janë më të mirat) dhe janë jashtëzakonisht të dobishme për ndarjen e të dhënave midis proceseve. Radhët janë veçanërisht të dobishme kur kalohen si parametër në një funksion objektiv të procesit për të mundësuar që procesi të konsumojë të dhëna. Duke përdorur funksionin put() mund të futim të dhëna në radhë dhe duke përdorur get() mund të marrim artikuj nga radhët. Shih kodin e mëposhtëm për një shembull të shpejtë.

from multiprocessing import Queue

colors = ['red', 'green', 'blue', 'black']
cnt = 1
# instantiating a queue object
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

Klasa e kyçjes së shumëpërpunimit të Python

Detyra e klasës Lock është mjaft e thjeshtë. Ai lejon kodin të pretendojë bllokimin në mënyrë që asnjë proces tjetër të mos mund të ekzekutojë kodin e ngjashëm derisa bllokimi të jetë lëshuar. Pra, detyra e klasës Lock është kryesisht dy. Njëra është të pretendosh bllokimin dhe tjetra është të lëshosh bllokimin. Për të pretenduar bllokimin, përdoret funksioni acquire() dhe për lirimin e bllokimit përdoret funksioni release().

Shembull i shumëpërpunimit të Python

Në këtë shembull të shumëpërpunimit të Python, ne do të bashkojmë të gjitha njohuritë tona së bashku. Supozoni se kemi disa detyra për të përmbushur. Për të kryer këtë detyrë, ne do të përdorim disa procese. Pra, ne do të mbajmë dy radhë. Njëra do të përmbajë detyrat dhe tjetra do të përmbajë regjistrin e detyrave të përfunduara. Pastaj ne instantojmë proceset për të përfunduar detyrën. Vini re se klasa python Queue tashmë është e sinkronizuar. Kjo do të thotë, ne nuk kemi nevojë të përdorim klasën Lock për të bllokuar një proces të shumëfishtë për të hyrë në të njëjtin objekt në radhë. Kjo është arsyeja pse, ne nuk kemi nevojë të përdorim klasën Lock në këtë rast. Më poshtë është implementimi ku po shtojmë detyra në radhë, më pas krijojmë procese dhe i nisim ato, më pas përdorim join() për të përfunduar proceset. Më në fund po shtypim regjistrin nga radha e dytë.

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))

    # creating processes
    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()

    # completing process
    for p in processes:
        p.join()

    # print the output
    while not tasks_that_are_done.empty():
        print(tasks_that_are_done.get())

    return True


if __name__ == '__main__':
    main()

Pishina e shumëpërpunimit të Python

Python Multiprocessing Pool mund të përdoret për ekzekutimin paralel të një funksioni nëpër vlera të shumta hyrëse, duke shpërndarë të dhënat hyrëse nëpër procese (paralelizmi i të dhënave). Më poshtë është një shembull i thjeshtë i Pool multiprocessing 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()