Patrice Ferlet
Patrice Ferlet
Créateur de ce blog.
Publié le juin 3, 2012 Temps de lecture: 4 min

Me suivre sur Mastodon

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.

comments powered by Disqus