Nehmen wir an, ich habe eine Schleife in Bash:
for foo in `some-command`
do
do-something $foo
done
do-something
ist CPU-gebunden und ich habe einen schönen glänzenden 4-Kern-Prozessor. Ich möchte in der Lage sein, bis zu 4 Sekunden gleichzeitig zu laufen do-something
.
Der naive Ansatz scheint zu sein:
for foo in `some-command`
do
do-something $foo &
done
Das wird laufen alle do-something
s auf einmal, aber es gibt ein paar Nachteile, vor allem das tun-was vielleicht auch einige bedeutende I / O haben , die Durchführung alle auf einmal vielleicht ein bisschen langsamer. Das andere Problem ist, dass dieser Codeblock sofort zurückgegeben wird, sodass keine andere Arbeit mehr möglich ist, wenn alle do-something
s fertig sind.
Wie würden Sie diese Schleife schreiben, damit immer X gleichzeitig do-something
laufen?
Antworten:
Je nachdem, was Sie tun möchten, kann xargs auch helfen (hier: Konvertieren von Dokumenten mit pdf2ps):
Aus den Dokumenten:
quelle
find [...] -print0
undxargs -0
.cpus=$(getconf _NPROCESSORS_ONLN)
--max-procs=0
, um so viele Prozesse wie möglich zu erhalten?--max-procs=0
ist eher wie der Versuch des Fragestellers (starten Sie so viele Prozesse wie Argumente).Mit GNU Parallel http://www.gnu.org/software/parallel/ können Sie schreiben:
GNU Parallel unterstützt auch das Ausführen von Jobs auf Remotecomputern. Auf den Remotecomputern wird eine pro CPU-Kern ausgeführt - auch wenn sie eine unterschiedliche Anzahl von Kernen haben:
Ein fortgeschritteneres Beispiel: Hier listen wir die Dateien auf, auf denen my_script ausgeführt werden soll. Dateien haben die Erweiterung (möglicherweise .jpeg). Wir möchten, dass die Ausgabe von my_script neben den Dateien in basename.out (z. B. foo.jpeg -> foo.out) platziert wird. Wir möchten my_script einmal für jeden Kern des Computers ausführen und es auch auf dem lokalen Computer ausführen. Für die Remotecomputer soll die zu verarbeitende Datei auf den angegebenen Computer übertragen werden. Wenn my_script fertig ist, möchten wir, dass foo.out zurück übertragen wird und dass foo.jpeg und foo.out vom Remotecomputer entfernt werden:
GNU Parallel stellt sicher, dass die Ausgabe von jedem Job nicht gemischt wird, sodass Sie die Ausgabe als Eingabe für ein anderes Programm verwenden können:
Weitere Beispiele finden Sie in den Videos: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
quelle
find
Befehl zum Generieren einer Dateiliste verwenden, da dies nicht nur das Problem verhindert, wenn in einem Dateinamen ein Leerzeichen vorhanden ist,for i in ...; do
sondern auch find,find -name \*.extension1 -or -name \*.extension2
was GNU parallel {.} Sehr gut verarbeiten kann.cat
natürlich nutzlos ist.quelle
Hier eine alternative Lösung, die in .bashrc eingefügt und für den täglichen Einzeiler verwendet werden kann:
Um es zu verwenden, muss man nur
&
nach den Jobs und einem pwait-Aufruf setzen. Der Parameter gibt die Anzahl der parallelen Prozesse an:Es wäre besser zu verwenden, als
wait
auf die Ausgabe vonjobs -p
zu warten, aber es scheint keine offensichtliche Lösung zu geben, zu warten, bis einer der angegebenen Jobs beendet ist, anstatt alle.quelle
Verwenden Sie anstelle einer einfachen Bash ein Makefile und geben Sie dann die Anzahl der gleichzeitigen Jobs an,
make -jX
wobei X die Anzahl der Jobs ist, die gleichzeitig ausgeführt werden sollen.Oder Sie können
wait
("man wait
") verwenden: mehrere untergeordnete Prozesse starten, aufrufenwait
- es wird beendet, wenn die untergeordneten Prozesse beendet sind.Wenn Sie das Ergebnis des Jobs speichern müssen, weisen Sie das Ergebnis einer Variablen zu. Nachdem
wait
Sie nur überprüft haben, was die Variable enthält.quelle
Versuchen Sie vielleicht ein Parallelisierungsprogramm, anstatt die Schleife neu zu schreiben? Ich bin ein großer Fan von XJobs. Ich verwende ständig xjobs, um Dateien in unserem Netzwerk massenweise zu kopieren, normalerweise beim Einrichten eines neuen Datenbankservers. http://www.maier-komor.de/xjobs.html
quelle
Während Sie dies richtig machen
bash
es wahrscheinlich unmöglich ist, , können Sie ein Semi-Recht ziemlich einfach machen.bstark
gab eine faire Annäherung an das Recht, aber seine hat die folgenden Mängel:Eine andere Annäherung, die diese Mängel nicht aufweist, ist die folgende:
Beachten Sie, dass dieser leicht anpassbar ist, um auch den Beendigungscode jedes Jobs am Ende zu überprüfen, damit Sie den Benutzer warnen können, wenn ein Job fehlschlägt, oder einen Beendigungscode für festlegen können
scheduleAll
die Anzahl der fehlgeschlagenen Jobs .Das Problem mit diesem Code ist genau das:
Eine Lösung, die sich um dieses letzte Problem kümmert, müsste verwenden,
kill -0
um abzufragen, ob einer der Prozesse anstelle des verschwunden ist,wait
und den nächsten Job planen. Dies führt jedoch zu einem kleinen neuen Problem: Sie haben eine Race-Bedingung zwischen dem Beenden eines Jobs und derkill -0
Überprüfung, ob es beendet ist. Wenn der Job beendet wurde und gleichzeitig ein anderer Prozess auf Ihrem System gestartet wird, wird eine zufällige PID verwendet, die zufällig die des gerade beendeten Jobs istkill -0
nicht bemerkt und die Dinge werden erneut unterbrochen.Eine perfekte Lösung ist in nicht möglich
bash
.quelle
Wenn Sie mit dem vertraut sind
make
Befehl , können Sie die Liste der Befehle, die Sie ausführen möchten, meistens als Makefile ausdrücken. Wenn Sie beispielsweise $ SOME_COMMAND für Dateien * .input ausführen müssen, von denen jede * .output erzeugt, können Sie das Makefile verwendenund dann einfach rennen
höchstens NUMBER-Befehle parallel ausführen.
quelle
Funktion für Bash:
mit:
quelle
make -j
ist klug, aber ohne Erklärung und diesen Klumpen von Nur-Schreib-Awk-Code verzichte ich auf Upvoting.Das Projekt, an dem ich arbeite, verwendet den Befehl wait , um parallele Shell-Prozesse (tatsächlich ksh) zu steuern. Um Ihre Bedenken hinsichtlich E / A auf einem modernen Betriebssystem auszuräumen, ist es möglich, dass die parallele Ausführung die Effizienz tatsächlich erhöht. Wenn alle Prozesse dieselben Blöcke auf der Festplatte lesen, muss nur der erste Prozess die physische Hardware treffen. Die anderen Prozesse können den Block häufig aus dem Festplatten-Cache des Betriebssystems im Speicher abrufen. Offensichtlich ist das Lesen aus dem Speicher mehrere Größenordnungen schneller als das Lesen von der Festplatte. Der Vorteil erfordert auch keine Codierungsänderungen.
quelle
Dies mag für die meisten Zwecke gut genug sein, ist aber nicht optimal.
quelle
So habe ich es geschafft, dieses Problem in einem Bash-Skript zu lösen:
quelle
Wirklich spät zur Party hier, aber hier ist eine andere Lösung.
Viele Lösungen verarbeiten keine Leerzeichen / Sonderzeichen in den Befehlen, lassen N Jobs nicht immer laufen, essen CPU in Besetztschleifen oder verlassen sich auf externe Abhängigkeiten (z
parallel
. B. GNU ).Mit Inspiration für die Handhabung von Dead / Zombie-Prozessen ist hier eine reine Bash-Lösung:
Und Beispielnutzung:
Die Ausgabe:
Für die prozessübergreifende Ausgabe
$$
kann die Protokollierung in einer Datei verwendet werden, zum Beispiel:Ausgabe:
quelle
Sie können eine einfache verschachtelte for-Schleife verwenden (ersetzen Sie N und M unten durch geeignete Ganzzahlen):
Dies führt N * M-mal in M Runden aus, wobei jede Runde N Jobs parallel ausführt. Sie können N gleich der Anzahl Ihrer CPUs machen.
quelle
Meine Lösung, um immer eine bestimmte Anzahl von Prozessen am Laufen zu halten, Fehler zu verfolgen und ununterbrochene / Zombie-Prozesse zu verarbeiten:
Verwendung:
quelle
$ DOMAINS = "Liste einiger Domänen in Befehlen" für foo in
some-command
dogetan
Ndomains =
echo $DOMAINS |wc -w
für i in $ (seq 1 1 $ Ndomains) echo "warte auf $ {job [$ i]}" warte "$ {job [$ i]}" erledigt
in diesem Konzept wird für die Parallelisierung arbeiten. Wichtig ist, dass die letzte Zeile der Bewertung '&' ist, wodurch die Befehle in den Hintergrund gestellt werden.
quelle