В двух словах легко запомнить PECS
<? extends T>
, если вам нужно извлечь объект типа T
из коллекции. <? super T>
, если вам нужно поместить объекты типа T
в коллекцию. В зависимости от того, что вы хотите сделать, xargs также может помочь (здесь: преобразование документов с помощью pdf2ps):
cpus=$( ls -d /sys/devices/system/cpu/cpu[[:digit:]]* | wc -w )
find . -name \*.pdf | xargs --max-args=1 --max-procs=$cpus pdf2ps
Из документации:
--max-procs=max-procs
-P max-procs
Run up to max-procs processes at a time; the default is 1.
If max-procs is 0, xargs will run as many processes as possible at a
time. Use the -n option with -P; otherwise chances are that only one
exec will be done.
Проект я работаю над использованием эти , ожидает команда для управления параллельной оболочкой (ksh на самом деле) процессы. Для обращения к опасениям по поводу IO, на современной ОС, это - возможное параллельное выполнение, на самом деле увеличит эффективность. Если все процессы читают те же блоки на диске, только первый процесс должен будет поразить физическое оборудование. Другие процессы часто будут в состоянии получить блок от дискового кэша ОС в памяти. Очевидно, чтение из памяти является несколькими порядками величины, более быстрыми, чем чтение из диска. Кроме того, преимущество не требует никаких изменений кодирования.
Возможно, попробуйте утилиту параллелизации, вместо этого переписав цикл? Я - большой поклонник xjobs. Я использую xjobs все время для массовых файлов копии через нашу сеть, обычно при установке нового сервера базы данных. http://www.maier-komor.de/xjobs.html
Вместо простого удара используйте Make-файл, затем определите количество одновременных заданий с make -jX
, где X количество заданий для выполнения сразу.
Или можно использовать wait
(" man wait
"): запустите несколько дочерних процессов, звоните wait
- это выйдет, когда дочерние процессы закончатся.
maxjobs = 10
foreach line in `cat file.txt` {
jobsrunning = 0
while jobsrunning < maxjobs {
do job &
jobsrunning += 1
}
wait
}
job ( ){
...
}
, Если необходимо сохранить результат задания, затем присвойте их результат переменной. После wait
Вы просто проверяете то, что содержит переменная.
maxjobs=4 parallelize () { while [ $# -gt 0 ] ; do jobcnt=(`jobs -p`) if [ ${#jobcnt[@]} -lt $maxjobs ] ; then do-something $1 & shift else sleep 1 fi done wait } parallelize arg1 arg2 "5 args to third job" arg4 ...
Хотя сделать это правильно в bash
, вероятно, невозможно, вы можете довольно легко сделать полуправо. bstark
дал хорошее приближение к праву, но у него есть следующие недостатки:
Другое приближение, которое не имеет этих Недостатки следующие:
scheduleAll() {
local job i=0 max=4 pids=()
for job; do
(( ++i % max == 0 )) && {
wait "${pids[@]}"
pids=()
}
bash -c "$job" & pids+=("$!")
done
wait "${pids[@]}"
}
Обратите внимание, что этот легко адаптируется, чтобы также проверять код выхода каждого задания при его завершении, чтобы вы могли предупреждать пользователя, если задание не удается, или установить код выхода для scheduleAll
в соответствии с количеством задание, которое не удалось, или что-то в этом роде.
Проблема с этим кодом как раз в том, что:
Решение, которое решает эту последнюю проблему, должно использовать kill -0
для опроса, не исчез ли какой-либо из процессов, вместо ожидания
и планирования следующего задания. Однако это создает небольшую новую проблему: у вас есть состояние гонки между окончанием задания, и kill -0
проверяет, закончился ли он. Если задание завершилось, и в то же время запускается другой процесс в вашей системе, принимая случайный PID, который является идентификатором только что завершенного задания, kill -0
не заметит, что ваша работа имеет закончено, и все снова сломается.
Идеальное решение невозможно в bash
.
Вот альтернативное решение, которое можно вставить в .bashrc и использовать для повседневного использования одного лайнера:
function pwait() {
while [ $(jobs -p | wc -l) -ge $1 ]; do
sleep 1
done
}
Чтобы использовать его, все, что нужно сделать, это поставить &
после задания и вызов pwait, параметр дает количество параллельных процессов:
for i in *; do
do_something $i &
pwait 10
done
Было бы лучше использовать wait
вместо ожидания занятости на выходе jobs -p
, но, похоже, нет очевидного решения - дождаться завершения любого из заданных заданий вместо их всех.
If you're familiar with the make
command, most of the time you can express the list of commands you want to run as a a makefile. For example, if you need to run $SOME_COMMAND on files *.input each of which produces *.output, you can use the makefile
INPUT = a.input b.input OUTPUT = $(INPUT:.input=.output) %.output : %.input $(SOME_COMMAND) $< $@ all: $(OUTPUT)
and then just run
make -j<NUMBER>
to run at most NUMBER commands in parallel.
С помощью GNU Parallel http://www.gnu.org/software/parallel/ вы можете написать:
some-command | parallel do-something
GNU Parallel также поддерживает выполнение заданий на удаленных компьютерах. При этом задания будут выполняться по одному на каждое ядро процессора на удаленных компьютерах - даже если у них разное количество ядер:
some-command | parallel -S server1,server2 do-something
Более продвинутый пример: Здесь мы перечисляем файлы, которые мы хотим, чтобы my_script выполнял. Файлы имеют расширение (возможно, .jpeg). Мы хотим, чтобы вывод my_script был помещен рядом с файлами в basename.out (например, foo.jpeg -> foo.out). Мы хотим запустить my_script один раз для каждого ядра компьютера, и мы хотим запустить его на локальном компьютере тоже. Для удаленных компьютеров мы хотим, чтобы обрабатываемый файл передавался на данный компьютер. Когда my_script завершит работу, мы хотим, чтобы foo.out был передан обратно, а затем мы хотим, чтобы foo.jpeg и foo.out были удалены с удаленного компьютера:
cat list_of_files | \
parallel --trc {.}.out -S server1,server2,: \
"my_script {} > {.}.out"
GNU Parallel следит за тем, чтобы вывод каждого задания не смешивался, поэтому вы можете использовать вывод как вход для другой программы:
some-command | parallel do-something | postprocess
Дополнительные примеры смотрите на видео: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1