Introduction à multiprocessing en Python

Le module multiprocessing en Python est une puissante bibliothèque qui permet l’exécution concurrente de processus. Il contourne le Global Interpreter Lock (GIL) en utilisant des sous-processus au lieu des threads et offre une programmation parallèle locale et à distance.

Le module multiprocessing offre une interface qui ressemble à celle du module threading. Cependant, il utilise des processus au lieu des threads. Un avantage clé de cela est que les processus sont indépendants et ne partagent pas la mémoire, évitant ainsi les problèmes de synchronisation.

Cependant, si les processus doivent partager des informations, comme dans le cas de tâches de calcul qui nécessitent le partage de résultats, le module multiprocessing offre des moyens de faire cela.

Dans le contexte de Python, multiprocessing est souvent utilisé pour le parallélisme de données, où une grande quantité de données est divisée en plus petites parties, et chaque partie est traitée en parallèle.

Dans les sections suivantes, nous explorerons plus en détail comment utiliser les fonctions map et map_async du module multiprocessing pour effectuer des tâches en parallèle.

Comprendre la fonction map en multiprocessing

La fonction map() est l’une des fonctions les plus couramment utilisées dans le module multiprocessing. Elle est conçue pour simplifier le processus de distribution des tâches entre plusieurs processus.

La fonction map() prend deux arguments principaux : une fonction et un itérable. Elle applique la fonction à chaque élément de l’itérable, en parallèle, et renvoie une liste des résultats. Voici un exemple de base :

from multiprocessing import Pool

def square(n):
    return n * n

if __name__ == "__main__":
    with Pool() as p:
        result = p.map(square, [1, 2, 3, 4, 5])
    print(result)

Dans cet exemple, la fonction square() est appliquée à chaque élément de la liste [1, 2, 3, 4, 5]. Chaque appel à la fonction square() est effectué dans un processus séparé, ce qui permet d’exécuter les calculs en parallèle.

Il est important de noter que la fonction map() est bloquante, ce qui signifie qu’elle ne retourne pas le contrôle à la ligne suivante du programme tant que tous les processus n’ont pas terminé leur exécution. Cela peut être un inconvénient si vous avez besoin de continuer à exécuter d’autres parties de votre programme pendant que les processus sont en cours d’exécution.

Dans la section suivante, nous explorerons la fonction map_async(), qui est une version non bloquante de map().

Découvrir map_async en multiprocessing

La fonction map_async() est une version non bloquante de la fonction map(). Elle fonctionne de manière similaire à map(), mais elle retourne immédiatement un objet AsyncResult sans attendre que tous les processus aient terminé.

Voici un exemple de base :

from multiprocessing import Pool

def square(n):
    return n * n

if __name__ == "__main__":
    with Pool() as p:
        result = p.map_async(square, [1, 2, 3, 4, 5])
    print(result.get())

Dans cet exemple, la fonction square() est appliquée à chaque élément de la liste [1, 2, 3, 4, 5] de la même manière que dans l’exemple précédent. Cependant, map_async() retourne immédiatement un objet AsyncResult. Pour obtenir les résultats réels, vous devez appeler la méthode get() sur cet objet.

L’avantage de map_async() est que vous pouvez continuer à exécuter d’autres parties de votre programme pendant que les processus sont en cours d’exécution. Cependant, il est important de noter que si vous appelez get() avant que tous les processus aient terminé, get() bloquera jusqu’à ce que tous les processus aient terminé.

Dans la section suivante, nous comparerons map() et map_async() plus en détail.

Comparaison entre map et map_async

Les fonctions map et map_async du module multiprocessing en Python sont deux outils puissants pour la parallélisation des tâches. Bien qu’elles soient similaires dans leur fonctionnement, il existe des différences clés entre elles.

Blocage vs Non-blocage

La différence la plus notable entre map et map_async est que map est une fonction bloquante, tandis que map_async est non bloquante. Cela signifie que lorsque vous appelez map, le programme attend que tous les processus aient terminé avant de continuer. En revanche, map_async retourne immédiatement un objet AsyncResult et le programme peut continuer à exécuter d’autres tâches.

Récupération des résultats

Avec map, les résultats sont immédiatement disponibles sous forme de liste. Avec map_async, les résultats sont encapsulés dans un objet AsyncResult. Pour obtenir les résultats, vous devez appeler la méthode get() sur l’objet AsyncResult. Notez que get() est une opération bloquante.

Ordre des résultats

map et map_async garantissent tous deux que l’ordre des résultats correspond à l’ordre des tâches d’entrée. Cela signifie que le premier résultat dans la liste de résultats correspond à la première tâche dans la liste de tâches, et ainsi de suite.

Conclusion

Le choix entre map et map_async dépend de vos besoins spécifiques. Si vous avez besoin que votre programme attende que toutes les tâches soient terminées avant de continuer, utilisez map. Si vous voulez que votre programme continue à exécuter d’autres tâches pendant que les processus sont en cours d’exécution, utilisez map_async.

Exemples pratiques d’utilisation de map et map_async

Voici quelques exemples pratiques qui illustrent comment utiliser map et map_async dans le module multiprocessing en Python.

Utilisation de map

from multiprocessing import Pool

def square(n):
    return n * n

if __name__ == "__main__":
    with Pool() as p:
        result = p.map(square, range(10))
    print(result)

Dans cet exemple, nous utilisons map pour appliquer la fonction square à chaque nombre de 0 à 9. Le résultat est une liste des carrés de ces nombres.

Utilisation de map_async

from multiprocessing import Pool

def square(n):
    return n * n

if __name__ == "__main__":
    with Pool() as p:
        result = p.map_async(square, range(10))
    print(result.get())

Dans cet exemple, nous utilisons map_async de la même manière que map. Cependant, map_async retourne un objet AsyncResult, donc nous devons appeler get() pour obtenir les résultats réels.

Ces exemples montrent comment map et map_async peuvent être utilisés pour effectuer des tâches en parallèle en Python. Ils sont particulièrement utiles lorsque vous avez une grande quantité de données à traiter et que vous voulez diviser cette charge de travail entre plusieurs processus.

Erreurs courantes et comment les éviter

Lors de l’utilisation du module multiprocessing en Python, il y a plusieurs erreurs courantes que vous pourriez rencontrer. Voici quelques-unes de ces erreurs et comment les éviter.

1. Ne pas utiliser if __name__ == "__main__":

Lorsque vous utilisez le module multiprocessing, vous devez toujours encapsuler votre code principal dans un bloc if __name__ == "__main__":. Si vous ne le faites pas, vous risquez de rencontrer une erreur de récursion infinie.

from multiprocessing import Pool

def square(n):
    return n * n

# Incorrect
with Pool() as p:
    result = p.map(square, range(10))
print(result)

# Correct
if __name__ == "__main__":
    with Pool() as p:
        result = p.map(square, range(10))
    print(result)

2. Essayer d’accéder à des variables globales

Les processus créés par le module multiprocessing n’ont pas accès aux variables globales du processus parent. Si vous essayez d’accéder à une variable globale dans un processus, vous obtiendrez une copie de la variable, pas la variable elle-même.

from multiprocessing import Pool

# Incorrect
x = 5
def add_x(n):
    return n + x

if __name__ == "__main__":
    with Pool() as p:
        result = p.map(add_x, range(10))
    print(result)

# Correct
def add_x(n):
    x = 5
    return n + x

if __name__ == "__main__":
    with Pool() as p:
        result = p.map(add_x, range(10))
    print(result)

3. Oublier de fermer ou de joindre les processus

Lorsque vous créez un processus, vous devez toujours vous assurer de le fermer ou de le joindre. Si vous ne le faites pas, le processus continuera à s’exécuter en arrière-plan, ce qui peut entraîner une consommation excessive de ressources.

from multiprocessing import Pool

def square(n):
    return n * n

# Incorrect
p = Pool()
result = p.map(square, range(10))
print(result)

# Correct
if __name__ == "__main__":
    with Pool() as p:
        result = p.map(square, range(10))
    print(result)

En évitant ces erreurs courantes, vous pouvez utiliser efficacement le module multiprocessing pour paralléliser vos tâches en Python.

Conclusion : Quand utiliser map vs map_async

Le choix entre map et map_async dépend principalement de la structure de votre programme et de vos besoins spécifiques en matière de parallélisation.

Utilisez map lorsque :

  • Vous avez une collection de tâches indépendantes à exécuter en parallèle, et vous avez besoin de collecter les résultats avant de passer à la suite du programme.
  • Vous n’avez pas besoin d’exécuter d’autres tâches en parallèle avec les tâches que vous distribuez avec map.

Utilisez map_async lorsque :

  • Vous avez une collection de tâches indépendantes à exécuter en parallèle, mais vous ne voulez pas bloquer l’exécution du reste de votre programme pendant que ces tâches sont en cours d’exécution.
  • Vous avez d’autres tâches qui peuvent être exécutées en parallèle avec les tâches que vous distribuez avec map_async.

En conclusion, le module multiprocessing en Python offre des outils puissants pour la parallélisation des tâches. En comprenant comment fonctionnent map et map_async, et quand utiliser l’un ou l’autre, vous pouvez écrire des programmes Python plus efficaces et plus performants.

By laurent

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *