web-dev-qa-db-fra.com

Quatre tâches en parallèle ... Comment puis-je faire ça?

J'ai une bande d'images PNG sur un répertoire. J'ai une application appelée pngout que je cours pour compresser ces images. Cette application s'appelle par un script que j'ai fait. Le problème est que ce script en fait une à la fois, quelque chose comme ça:

FILES=(./*.png)
for f in  "${FILES[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 $f R${f/\.\//}
done

Traitement d'un seul fichier à la fois, prend beaucoup de temps. Après avoir exécuté cette application, je vois que la CPU n'est que de 10%. J'ai donc découvert que je peux diviser ces fichiers en 4 lots, mettre chaque lot dans un répertoire et un incendie 4, à partir de quatre fenêtres terminales, quatre processus, donc j'ai quatre cas de mon script, en même temps, en train de traiter ces images et le Le travail prend 1/4 du temps.

Le deuxième problème est que j'ai perdu du temps à diviser les images et les lots et la copie du script à quatre répertoires, ouvrez 4 fenêtres terminales, bla bla ...

Comment cela avec un script, sans avoir à diviser quoi que ce soit?

Je veux dire deux choses: d'abord comment je suis depuis un script Bash, déclencher un processus à l'arrière-plan? (Ajoutez simplement et à la fin?) Deuxièmement: comment puis-je arrêter d'envoyer des tâches à l'arrière-plan après avoir envoyé les quatrième tâches et mettre le script attendre jusqu'à la fin des tâches? Je veux dire, il suffit d'envoyer une nouvelle tâche à l'arrière-plan comme fin des tâches, gardant toujours 4 tâches en parallèle? Si je ne fais pas que la boucle ne fera pas de zillions de tâches à l'arrière-plan et que la CPU va obstruera.

23
SpaceDog

Si vous avez une copie de xargs qui prend en charge l'exécution parallèle avec -P, vous pouvez simplement faire

printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}

Pour d'autres idées, le WINLEDED BASH WIKI a une section dans l'article de gestion de processus décrivant exactement ce que vous voulez.

33
jw013

Outre les solutions déjà proposées, vous pouvez créer un makefile qui explique comment créer un fichier compressé à partir de non compressé et d'utiliser make -j 4 Pour exécuter 4 emplois en parallèle. Le problème est que vous devez nommer différemment des fichiers compressés et non compressés, ou les stocker dans différents répertoires, sinon la rédaction d'une règle de fabrication raisonnable sera impossible.

8
9000

Pour répondre à vos deux questions:

  • oui, l'ajout et à la fin de la ligne vous informeront que Shell de lancer un processus d'arrière-plan.
  • utilisation de la commande wait, vous pouvez demander à la coquille d'attendre que tous les processus de l'arrière-plan se terminent avant de continuer.

Voici le script modifié de sorte que j est utilisé pour suivre le nombre de processus d'arrière-plan. Lorsque NB_CONCURRENT_PROCESSES est atteint, le script réinitialisera j à 0 et attendre que tous les processus d'arrière-plan se terminent avant de reprendre son exécution.

files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 "$f" R"${f/\.\//}" &
        ((++j == nb_concurrent_processes)) && { j=0; wait; }
done
5
Frederik Deweerdt