Parallele Ausführung von Befehlen mit einer Begrenzung der gleichzeitigen Anzahl von Befehlen

18288
Vi.

Sequenziell: for i in ; do do_something $i; done- zu langsam

Parallel: for i in ; do do_something $i& done- zu viel Last

Wie lassen sich Befehle parallel ausführen, jedoch nicht mehr als beispielsweise 20 Instanzen pro Moment?

Jetzt normalerweise mit Hack wie for i in ; do do_something $i& sleep 5; done, aber das ist keine gute Lösung.

Update 2 : Konvertierte die akzeptierte Antwort in ein Skript: http://vi-server.org/vi/parallel

#!/bin/bash  NUM=$1; shift  if [ -z "$NUM" ]; then echo "Usage: parallel <number_of_tasks> command" echo " Sets environment variable i from 1 to number_of_tasks" echo " Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override." echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'" exit 1 fi  export CMD="$@";  true $  cat << EOF | make -f - -s $MAKEOPTS PHONY=jobs jobs=\$(shell echo )  all: \$  \$: i=\$@ sh -c "\$\$CMD" EOF 

Beachten Sie, dass Sie vor "i =" 8 Leerzeichen durch zwei Tabulatoren ersetzen müssen, damit es funktioniert.

17

7 Antworten auf die Frage

11
Ole Tange

GNU Parallel ist dafür gemacht.

seq 1 1000 | parallel -j20 do_something 

Es kann sogar Jobs auf Remote-Computern ausführen. Hier ein Beispiel für die Umcodierung einer MP3-Datei in OGG mit server2 und einem lokalen Computer, auf dem pro CPU-Kern 1 Job ausgeführt wird:

parallel --trc {.}.ogg -j+0 -S server2,: \ 'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3 

Sehen Sie hier ein Einführungsvideo zu GNU Parallel:

http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ich habe keine Ahnung von "moreutils" und dass es bereits ein Werkzeug für den Job gibt. Schauen und vergleichen. Vi. vor 14 Jahren 0
Das "Parallel" in moreutils ist nicht GNU Parallel und ist in seinen Optionen ziemlich eingeschränkt. Der obige Befehl wird nicht mit der Parallele von moreutils ausgeführt. Ole Tange vor 14 Jahren 1
Eine weitere Option: `xargs --max-procs = 20`. Vi. vor 8 Jahren 1
4
Benjamin Bannier

Not a bash solution, but you should use a Makefile, possibly with -l to not exceed some maximum load.

NJOBS=1000 .PHONY = jobs jobs = $(shell echo ) all: $(jobs) $(jobs): do_something $@ 

Then to start 20 jobs at a time do

$ make -j20 

or to start as many jobs as possible without exceeding a load of 5

$ make -j -l5 
Sieht aus wie die nicht hackige Lösung für jetzt. Vi. vor 14 Jahren 0
`echo -e 'PHONY = jobs \ njobs = $ (shell echo ) \ n \ nall: $ \ n \ n $ : \ n \ t \ techo $ @; sleep \ `echo $$ RANDOM / 6553 | bc -l \ `'| make -f - -j20` Jetzt sieht es wieder hackiger aus. Vi. vor 14 Jahren 2
@vi: oh mein .... Benjamin Bannier vor 14 Jahren 0
Konvertiert Ihre Lösung in ein Skript. Jetzt kann es problemlos verwendet werden. Vi. vor 14 Jahren 0
1
harrymc

One simple idea:

Check for i modulo 20 and execute the wait shell-command before do_something.

Entweder wird darauf gewartet, dass alle aktuellen Aufgaben abgeschlossen sind (das Erstellen von Sags in der Anzahl der Tasks) oder auf eine bestimmte Aufgabe, die länger dauern kann (in diesem Fall erneut). Vi. vor 14 Jahren 0
@Vi: Shell warten bezieht sich auf alle Hintergrundaufgaben, die zu dieser Shell gehören. harrymc vor 14 Jahren 0
1
msw
for i in ; do (echo $i ; sleep `expr $RANDOM % 5` ) & while [ `jobs | wc -l` -ge 20 ] ; do sleep 1 done done 
Kann `while [\` jobs | sein wc -l \ `-ge 20]; do` Vi. vor 14 Jahren 0
sicher, aber in meinem Beispiel muss ich dann 'njobs' zweimal berechnen, und Leistung ist in Shell-Skripts, die Schlafaufgaben ausführen, ziemlich wichtig;) msw vor 14 Jahren 0
Ich meine, Ihre Version funktioniert nicht wie erwartet. Ich ändere "sleep 1" in "sleep 0.1" und es beginnt, die Anzahl der Njobs auf 40-50 anstatt 20 zu erhöhen. Wenn mehr als 20 Jobs vorhanden sind, müssen wir warten, bis alle Jobs beendet sind, und nicht nur 1 Sekunde warten. Vi. vor 14 Jahren 0
1
Paul R

You could use ps to count how many processes you have running, and whenever this drops below a certain threshold you start another process.

Pseudo code:

i = 1 MAX_PROCESSES=20 NUM_TASKS=1000 do get num_processes using ps if num_processes < MAX_PROCESSES start process $i $i = $i + 1 endif sleep 1 # add this to prevent thrashing with ps until $i > NUM_TASKS 
1
warren

posting the script in the question with formatting:

#!/bin/bash NUM=$1; shift if [ -z "$NUM" ]; then echo "Usage: parallel <number_of_tasks> command" echo " Sets environment variable i from 1 to number_of_tasks" echo " Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override." echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'" exit 1 fi export CMD="$@"; true $ cat << EOF | make -f - -s $MAKEOPTS PHONY=jobs jobs=\$(shell echo ) all: \$ \$: i=\$@ sh -c "\$\$CMD" EOF 

Note that you must replace 8 spaces with 2 tabs before "i=".

0
ouyangyewei

Sie können es so machen.

threads=20 tempfifo=$PMS_HOME/$$.fifo  trap "exec 1000>&-;exec 1000<&-;exit 0" 2 mkfifo $tempfifo exec 1000<>$tempfifo rm -rf $tempfifo  for ((i=1; i<=$threads; i++)) do echo >&1000 done  for ((j=1; j<=1000; j++)) do read -u1000 { echo $j echo >&1000 } & done  wait echo "done!!!!!!!!!!" 

Bei Verwendung von Named Pipes werden jedes Mal 20 Sub-Shell parallel ausgeführt.

Hoffe es hilft :)