Recherche de site Web

Exemple de multiprocessus en Python


Dans notre tutoriel précédent, nous avons appris à propos de l'exemple CSV en Python. Dans ce tutoriel, nous allons apprendre le multiprocessing en Python avec des exemples.

Multiprocessing Python

Le traitement parallèle attire de plus en plus d'attention de nos jours. Si vous ne savez toujours pas ce qu'est le traitement parallèle, informez-vous sur Wikipedia. Alors que les fabricants de CPU ajoutent de plus en plus de cœurs à leurs processeurs, créer du code parallèle est un excellent moyen d'améliorer les performances. Python a introduit le module multiprocessing pour nous permettre d'écrire du code parallèle. Pour comprendre la principale motivation de ce module, nous devons connaître quelques bases sur la programmation parallèle. Après avoir lu cet article, nous espérons que vous serez capable de rassembler des connaissances sur ce sujet.

Processus de Multiprocessing Python, Queue et Verrous

Il y a de nombreuses classes dans le module de multiprocessus Python pour créer un programme parallèle. Parmi elles, trois classes de base sont Process, Queue et Lock. Ces classes vous aideront à construire un programme parallèle. Mais avant de décrire cela, commençons ce sujet avec un code simple. Pour rendre un programme parallèle utile, vous devez savoir combien de cœurs se trouvent dans votre PC. Le module de multiprocessus Python vous permet de le savoir. Le code simple suivant affichera le nombre de cœurs dans votre PC.

import multiprocessing

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

La sortie suivante peut varier pour votre PC. Pour moi, le nombre de cœurs est 8.

Classe Process de la bibliothèque multiprocessing de Python

La classe Process du module de multiprocessing Python est une abstraction qui crée un autre processus Python, lui permet d'exécuter du code et offre un moyen à l'application parente de contrôler l'exécution. Il y a deux fonctions importantes qui appartiennent à la classe Process - la fonction start() et la fonction join(). Tout d'abord, nous devons écrire une fonction qui sera exécutée par le processus. Ensuite, nous devons instancier un objet de processus. Si nous créons un objet de processus, rien ne se passera jusqu'à ce que nous le fassions démarrer via la fonction start(). Ensuite, le processus s'exécutera et retournera son résultat. Après cela, nous indiquons au processus de se compléter via la fonction join(). Sans l'appel de la fonction join(), le processus restera inactif et ne se terminera pas. Donc, si vous créez de nombreux processus et ne les terminez pas, vous pourriez faire face à une pénurie de ressources. Ensuite, vous devrez peut-être les tuer manuellement. Une chose importante est que, si vous voulez passer n'importe quel argument à travers le processus, vous devez utiliser l'argument clé args. Le code suivant sera utile pour comprendre l'utilisation de la classe 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()

La sortie du code suivant sera :

Classe Queue de la bibliothèque multiprocessing de Python

Vous avez une connaissance de base des structures de données informatiques, vous connaissez probablement les files d'attente. Le module de multiprocessus Python fournit la classe Queue qui est exactement une structure de données premier entré, premier sorti. Elles peuvent stocker n'importe quel objet Python sérialisé (bien que les simples soient les meilleurs) et sont extrêmement utiles pour partager des données entre plusieurs processus. Les files d'attente sont particulièrement utiles lorsqu'elles sont passées en tant que paramètre à la fonction cible d'un processus pour permettre au processus de consommer des données. En utilisant la fonction put(), nous pouvons insérer des données dans la file d'attente, et en utilisant get(), nous pouvons obtenir des éléments des files d'attente. Voir le code suivant pour un exemple rapide.

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

Classe Lock de la bibliothèque multiprocessing de Python

La tâche de la classe Lock est assez simple. Elle permet au code de revendiquer un verrou afin qu'aucun autre processus ne puisse exécuter le même code tant que le verrou n'a pas été libéré. Ainsi, la tâche de la classe Lock est principalement de deux types. L'un est de revendiquer le verrou et l'autre est de libérer le verrou. Pour revendiquer le verrou, la fonction acquire() est utilisée et pour libérer le verrou, la fonction release() est utilisée.

Exemple de multiprocessus en Python

Dans cet exemple de multiprocessus en Python, nous allons rassembler toutes nos connaissances. Supposons que nous ayons des tâches à accomplir. Pour réaliser cette tâche, nous utiliserons plusieurs processus. Ainsi, nous maintiendrons deux files d'attente. L'une contiendra les tâches et l'autre contiendra le journal des tâches complétées. Ensuite, nous instancions les processus pour accomplir la tâche. Notez que la classe Queue de Python est déjà synchronisée. Cela signifie que nous n'avons pas besoin d'utiliser la classe Lock pour bloquer plusieurs processus afin d'accéder au même objet de file d'attente. C'est pourquoi nous n'avons pas besoin d'utiliser la classe Lock dans ce cas. Voici l'implémentation où nous ajoutons des tâches à la file d'attente, puis créons des processus et les démarrons, puis utilisons join() pour compléter les processus. Enfin, nous imprimons le journal de la deuxième file d'attente.

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

En fonction du nombre de tâches, le code mettra un certain temps à vous montrer le résultat. La sortie du code suivant variera de temps en temps.

Pool de multiprocessing Python

Le Pool de multiprocessing de Python peut être utilisé pour l'exécution parallèle d'une fonction sur plusieurs valeurs d'entrée, en distribuant les données d'entrée à travers les processus (parallélisme des données). Voici un exemple simple de Pool de multiprocessing en 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()

L'image ci-dessous montre le résultat du programme ci-dessus. Remarquez que la taille du pool est de 2, donc deux exécutions de la fonction work_log se déroulent en parallèle. Lorsque l'une des fonctions de traitement se termine, elle prend le prochain argument, et ainsi de suite.