Pool de thread en bash
Créer un pool de thread est une opération toujours un peu complexe à mettre en place. Il faut réfléchir à plusieurs points: le nombre de thread concurrents, savoir attendre la fin du pool pour relancer une série d’opérations, bref… En bash, il existe deux méthodes peu complexes et qui vous permettront de ne pas pourrir vos ressources CPU.
Car voilà, on a de temps en temps envie de faire des scripts qui lancent des tâches de fond de manière répétitive… quand on en a moins de 10, on ne se pose pas la question… mais à partir de 20, 30 voir 40 threads… cela devient compliqué pour votre système.
Dernièrement, je voulais lancer près de 40 000 taches concurrentes… j’ai fait l’erreur de pense que ça allait être géré par l’OS… que nenni !
Deux solutions s’offrent à vous (voir 3, mais la méthode flock me parait un peu tordue), utiliser un pool manuel, ou utiliser une option de la commande xargs.
Méthode pool manuelle
Cette méthode est simple comme bonjour, on utilise une boucle + un compteur, et la commande “wait”. Cette dernière permet d’attendre la fin des tâches de fond avant de finir.
Voici la méthode:
MAXPROC=4
COUNT=0
for i in $(seq 20)
do
#command = thread avec & pour le mettre en tache de fond
command &
COUNT=$((COUNT+1))
#on attend si on atteind MAXPROC thread
if [[ $COUNT -gt $MAXPROC ]]; then
wait
COUNT=0
fi
done
Cette méthode est facile, assez propre et permet de gérer un pool aisément, la seule contrainte est qu’il faut que tous les processus dans le pool se termine pour en relancer de nouveaux. Rien de bien méchant… mais bon…
Méthode pool via xargs
Cette méthode est peu connue et pourtant… tellement efficace. “xargs” a une option peu connue: “–max-procs” qui permet de gérer un pool de processus.
“xargs” est un simple pont qui permet de récupérer une liste de chose à passer en argument à une autre commande. Ainsi:
echo "/tmp /var" | xargs ls
cela lancera 2 fois la commande “ls” avec en premier temps “/tmp” en argument, puis “/var”.
Mais xargs peut manipuler un peu les chaines et nous permettre de jouer un peu:
echo "ls ps df" | xargs -I CMD bash -c CMD
Cette exemple est un peu inutile, mais vous montre comment cela fonctionne. En gros, j’envois les commande “ls”, “ps” et “df” à xargs. Celui ci récupère l’entrée “-I” dans CMD. Donc CMD vaudra tour à tour “ls”, puis “ps” et enfin “df”. Cette chaine est envoyé à la commande “bash -c”
Vous suivez ?
Et bien imaginons qu’on veuille faire 10 opération, en gérant un pool. On va créer un fichier de commande, puis on le passera à xargs, en prenant soin de donner une valeur max de processus concurrent. Le fichier de commandes:
tar cfz /var/lib/mysql mysql.tgz
tar cfz /usr/local local.tgz
tar cfz ~/.config config.tgz
tar cfz ~/Documents Documents.tgz
tar cfz ~/Bureau Bureau.tgz
tar cfz ~/Vidéos Vidéos.tgz
tar cfz ~/Images Images.tgz
tar cfz ~/Musique Musique.tgz
tar cfz ~/Téléchargements Téléchargements.tgz
tar cfz ~/Projets Projets.tgz
Cela fait une bonne liste de choses à sauvegarder… et beaucoup de temps à attendre qu’un processus est terminé pour lancer l’autre. Le souci c’est que si je lance tout ça en parallèle, je vais plomber mes ressources système le temps qu’il finisse.
Voilà comment on s’y prend:
while read cmd; do echo $cmd; done < commands.txt | xargs -I CMD --max-procs=3 bash -c CMD
Et voilà, le fichier de commandes est lu, puis chaque ligne passe à xargs qui l’enverra à “bash -c” à condition de ne pas dépasser le pool de thread. Le cas échéant, il va attendre que son pool est de la place pour lancer une nouvelle commande en tâche de fond.
C’est pas si compliqué, et à la longue on s’en sert facilement. A vous d’adapter l’exemple pour vos opérations.