Параллельное выполнение команд с ограничением количества одновременных команд

17854
Vi.

Последовательный: for i in ; do do_something $i; done- слишком медленно

Параллельно: for i in ; do do_something $i& done- слишком большая нагрузка

Как выполнять команды параллельно, но не более, например, 20 экземпляров в минуту?

Сейчас обычно используют взломать, как for i in ; do do_something $i& sleep 5; done, но это не очень хорошее решение.

Обновление 2 : преобразовал принятый ответ в скрипт: 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 

Обратите внимание, что вы должны заменить 8 пробелов на 2 табуляции перед "i =", чтобы это работало.

17

7 ответов на вопрос

11
Ole Tange

GNU Parallel сделан для этого.

seq 1 1000 | parallel -j20 do_something 

Он даже может запускать задания на удаленных компьютерах. Вот пример перекодирования MP3 в OGG с использованием server2 и локального компьютера, выполняющего 1 задание на ядро ​​ЦП:

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

Смотрите вступительное видео для GNU Parallel здесь:

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

Не знал о «moreutils» и о том, что уже есть инструмент для работы. Смотря и сравнивая. Vi. 14 лет назад 0
Параллель в moreutils не является GNU Parallel и довольно ограничена в своих возможностях. Команда выше не будет работать с параллелью из moreutils. Ole Tange 14 лет назад 1
Еще один вариант: `xargs --max-procs = 20`. Vi. 8 лет назад 1
4
Benjamin Bannier

Не bash-решение, но вы должны использовать Makefile, возможно, -lчтобы он не превышал какую-то максимальную нагрузку.

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

Затем, чтобы начать 20 работ одновременно

$ make -j20 

или начать как можно больше заданий, не превышая нагрузку 5

$ make -j -l5 
Выглядит как нехакерское решение на данный момент. Vi. 14 лет назад 0
`echo -e 'PHONY = jobs \ njobs = $ (shell echo ) \ n \ nall: $ \ n \ n $ : \ n \ t \ techo $ @; сон \ `эхо $$ RANDOM / 6553 | bc -l \ `'| make -f - -j20` Теперь это выглядит более хакерским. Vi. 14 лет назад 2
@vi: о боже .... Benjamin Bannier 14 лет назад 0
Преобразовал ваше решение в скрипт. Теперь его можно использовать с легкостью. Vi. 14 лет назад 0
1
harrymc

Одна простая идея:

Проверьте по модулю 20 и выполните команду оболочки shell перед do_something.

Он будет либо ожидать завершения всех текущих задач (создавая провалы в графике количества задач), либо ждать одной конкретной задачи, которая может затянуться на более длительное время (снова создавая провалы в этом случае) Vi. 14 лет назад 0
@Vi: Ожидание оболочки - это все фоновые задачи, которые принадлежат этой оболочке. harrymc 14 лет назад 0
1
msw
for i in ; do  (echo $i ; sleep `expr $RANDOM % 5` ) & while [ `jobs | wc -l` -ge 20 ] ; do  sleep 1  done done 
Может быть `while [\` jobs | wc -l \ `-ge 20]; do`? Vi. 14 лет назад 0
конечно, но в моем примере мне пришлось бы дважды вычислять `njobs`, и производительность довольно важна в сценариях оболочки, которые запускают спящие задачи;) msw 14 лет назад 0
Я имею в виду, ваша версия не работает, как ожидалось. Я изменяю `sleep 1` на` sleep 0.1`, и он начинает усреднять njobs до 40-50 вместо 20. Если существует более 20 рабочих мест, нам нужно дождаться окончания любой работы, а не просто ждать 1 секунду. Vi. 14 лет назад 0
1
Paul R

Вы можете использовать, psчтобы подсчитать, сколько процессов у вас запущено, и всякий раз, когда это падает ниже определенного порога, вы запускаете другой процесс.

Псевдокод:

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

размещение сценария в вопросе с форматированием:

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

Обратите внимание, что перед «i =» вы должны заменить 8 пробелов на 2 табуляции.

0
ouyangyewei

you can do it like this.

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!!!!!!!!!!" 

using named pipes, every time, it runs 20 sub shell in parallel.

Hope it help :)

Похожие вопросы