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.