Ce billet est le petit frère du précédent billet Traitement batch multi-cœurs en PHP.
Pourquoi Python ? Utilisant désormais Python et PHP, j’ai voulu transposé cette fonction PHP en Python. Ça peut même servir à faire une comparaison rapide entre un code PHP et un code Python.
Le code Python est effectivement plus court (36 lignes contre 52 en PHP) et plus lisible, cette dernière affirmation restant un point de vue personnel.
Utilisation
C’est uniquement une fonction que je livre ici ! Si vous voulez vous en servir, il vous faudra rajouter du code (si peu) pour lui faire faire ce que vous voulez.
Le prototype de la fonction est simple :
def pool_execute(commandes,nb_max_process)
Les paramètres sont les suivants :
- commandes : Un tableau dont chaque entrée est une chaîne de caractères contenant une commande shell à exécuter ou une liste comme ce que propose la classe Popen du module subprocess.
- nb_max_process : Le nombre maximum de commandes pouvant être exécutées simultanément.
Exemple d’utilisation : Conversion de PNG en JPEG sur un dual-core
commandes=[ "convert photo1.png photo1.jpg", "convert photo2.png photo2.jpg", ["convert","photo3.png","photo3.jpg"] ] pool_execute(commandes,2)
Plutôt simple d’utilisation, non ?
L’appel à pool_execute ne retourne que lorsque tous les processus se sont terminés. C’est à vous de vous assurer qu’aucun processus ne va se retrouver dans une situation de boucle infinie…
Explications
La fonction pool_execute utilise un pool de processus. Il s’agit d’un banal tableau dont la taille est égale au nombre maximum de commandes à exécuter.
La boucle principale va ensuite s’activer toutes les 0,05 secondes pour voir si une nouvelle commande ne peut pas être lancée.
Pour cela, elle parcourt chaque entrée du pool (qui correspond à une commande exécutée) et pour chaque entrée, elle teste si le processus associé est encore en cours d’exécution. S’il ne l’est plus, on le vire du pool pour en installer un nouveau.
Un seul lancement de commande se fait par itération, toutes les 0,05 secondes.
La seconde boucle permet d’attendre les dernières commandes encore en cours d’exécution avant de rendre la main à l’appelant.
Pool_execute fait appel à la classe Popen pour gérer les processus. La méthode poll des objets générés permet de savoir si le processus est terminé. Contrairement à la version PHP, il n’est nul besoin de fermer le processus car on travaille avec des objets qui feront le ménage à leur destruction.
Code source
Cette fonction est à incorporer à vos scripts :
from subprocess import Popen
from time import sleep
def pool_execute(commandes,nb_max_process):
# Initialise le pool de processus
pool=[False]*nb_max_process
# Exécute toutes les commandes
while len(commandes)>0:
commande=commandes.pop(0)
# Essaie de lancer une commande
commande_lancee=False
while commande_lancee==False:
sleep(0.05)
# Recherche une entrée libre dans le pool
for i in range(0,nb_max_process):
# Teste si l'entrée a déjà été utilisée ou point sur un processus terminé
if not pool[i] or pool[i].poll()!=None:
pool[i]=Popen(commande,shell=(type(commande)==str))
commande_lancee=True
break
# Attend que toutes les commandes restantes se terminent
commande_restante=True
while commande_restante:
sleep(0.05)
# Recherche une commande encore en cours d'exécution dans le pool
commande_restante=False
for i in range(0,nb_max_process):
# Teste si l'entrée pointe sur un processus terminé
if pool[i] and pool[i].poll()==None:
commande_restante=True
break

