Frage

Um die CPU-Auslastung (I laufen die Dinge auf einem Debian Lenny in EC2) Ich habe ein einfaches Skript starten Aufträge parallel zu maximieren:

#!/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 &
...

Ich bin ganz mit dieser Arbeitslösung zufrieden, aber ich konnte nicht herausfinden, wie weiter Code zu schreiben, die alle Schleifen ausgeführt nur einmal abgeschlossen war.

Gibt es eine Möglichkeit der Kontrolle über diese zu bekommen?

War es hilfreich?

Lösung

Es gibt eine bash für diesen Befehl builtin.

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.

Andere Tipps

GNU Parallel wird Ihren Skript noch kürzer und möglicherweise effizienter machen:

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

Dies wird einen Job pro CPU-Kern läuft und weiterhin tun, bis alle Dateien verarbeitet werden.

Ihre Lösung wird geteilt grundsätzlich die Arbeitsplätze in Gruppen vor der Ausführung. Hier 32 Arbeitsplätze in 4 Gruppen:

Einfache Planung

GNU Parallel laicht stattdessen einen neuen Prozess, wenn man beendet - hält die CPUs aktiv und damit Zeitersparnis:

GNU Parallel Scheduling

Um mehr zu erfahren:

Ich hatte das vor kurzem zu tun und mit der folgenden Lösung endete:

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

Hier ist, wie es funktioniert:

wait -n Exits, sobald eine der (potenziell viele) Hintergrundjobs beendet. Es ergibt immer wahr, und die Schleife geht weiter, bis:

  1. Exit-Code 127: der letzte Hintergrundjob erfolgreich beendet. Im Dieser Fall, ignorieren wir die Exit-Code und zum Verlassen des Unterschale mit Code 0.
  2. Jede des Hintergrundjobs fehlgeschlagen. Wir verlassen gerade die Unterschale mit dem Exit-Code.

Mit set -e, das garantiert, dass das Skript vorzeitig beendet wird und den Exit-Code eines gescheiterten Hintergrundjobs durchlaufen.

Das ist meine rohe Lösung:

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
}

Die Idee ist, „Jobs“, um zu sehen, wie viele Kinder sind aktiv im Hintergrund und warten, bis diese Zahl fällt (ein Kind Exits) zu verwenden. Sobald ein Kind vorhanden ist, kann die nächste Aufgabe gestartet werden.

Wie Sie sehen können, gibt es auch ein paar zusätzliche Logik ist es, die gleichen Experimente / Befehle mehrere Male zu vermeiden läuft. Es ist der Job für mich .. Allerdings könnte diese Logik entweder ausgelassen oder weiter verbessert (zum Beispiel prüft Dateierstellung Zeitstempel, Eingabeparameter, usw.).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top