Question

Afin de maximiser l'utilisation du processeur (je lance des choses sur une Debian Lenny dans EC2) J'ai un script simple pour lancer des travaux en parallèle:

#!/bin/bash

for i in apache-200901*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200902*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200903*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200904*.log; do echo "Processing $i ..."; do_something_important; done &
...

Je suis tout à fait satisfait de cette solution de travail, mais je ne pouvais pas comprendre comment écrire du code supplémentaire qui n'exécuté une fois que toutes les boucles ont été complétées.

Est-il possible d'obtenir le contrôle de cette situation?

Était-ce utile?

La solution

Il y a une commande builtin bash pour cela.

wait [n ...]
      Wait for each specified process and return its termination  sta‐
      tus.   Each  n  may be a process ID or a job specification; if a
      job spec is given, all processes  in  that  job’s  pipeline  are
      waited  for.  If n is not given, all currently active child pro‐
      cesses are waited for, and the return  status  is  zero.   If  n
      specifies  a  non-existent  process or job, the return status is
      127.  Otherwise, the return status is the  exit  status  of  the
      last process or job waited for.

Autres conseils

Utilisation parallèle GNU fera votre script encore plus court et peut-être plus efficace:

parallel 'echo "Processing "{}" ..."; do_something_important {}' ::: apache-*.log

Ceci lancera un emploi par cœur de CPU et de continuer à le faire jusqu'à ce que tous les fichiers sont traités.

Votre solution sera essentiellement diviser les emplois en groupes avant d'exécuter. Ici 32 emplois en 4 groupes:

ordonnancement simple

GNU parallèle à la place engendre un nouveau processus quand on termine - en gardant les CPU active et un gain de temps:

programmation parallèle GNU

Pour en savoir plus:

Je devais le faire récemment et a fini avec la solution suivante:

while true; do
  wait -n || {
    code="$?"
    ([[ $code = "127" ]] && exit 0 || exit "$code")
    break
  }
done;

Voilà comment cela fonctionne:

sorties de wait -n dès que l'une des sorties (potentiellement nombreux) emplois de fond. Il est toujours vraie et la boucle se poursuit jusqu'à:

  1. Code de sortie 127: le dernier travail de fond est sorti avec succès. Dans ce cas, nous ignorons le code de sortie et de sortie du sous-shell avec le code 0.
  2. Tout du travail de fond a échoué. Nous sortons juste la sous-shell avec ce code de sortie.

Avec set -e, cela garantira que le script prendra fin tôt et passer à travers le code de sortie de tout travail de fond a échoué.

Ceci est ma solution brute:

function run_task {
        cmd=$1
        output=$2
        concurency=$3
        if [ -f ${output}.done ]; then
                # experiment already run
                echo "Command already run: $cmd. Found output $output"
                return
        fi
        count=`jobs -p | wc -l`
        echo "New active task #$count:  $cmd > $output"
        $cmd > $output && touch $output.done &
        stop=$(($count >= $concurency))
        while [ $stop -eq 1 ]; do
                echo "Waiting for $count worker threads..."
                sleep 1
                count=`jobs -p | wc -l`
                stop=$(($count > $concurency))
        done
}

L'idée est d'utiliser des « emplois » pour voir combien d'enfants sont actifs dans l'arrière-plan et d'attendre jusqu'à ce que ce nombre tombe (un sort de l'enfant). Une fois qu'un enfant existe, la tâche suivante peut être démarré.

Comme vous pouvez le voir, il y a aussi un peu de logique supplémentaire pour éviter de les mêmes expériences / commandes à plusieurs reprises. Il fait le travail pour moi .. Cependant, cette logique pourrait être soit sautées ou encore amélioré (par exemple, vérifier les horodatages de création de fichiers, paramètres d'entrée, etc.).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top