并行运行命令,同时限制命令数


23

顺序:for i in {1..1000}; do do_something $i; done-太慢

并行:for i in {1..1000}; do do_something $i& done-负载过多

如何并行运行命令,但每秒钟最多运行20个实例?

现在通常使用hack like for i in {1..1000}; 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 ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

请注意,必须使用“ i =“之前的2个制表符替换8个空格才能使其正常工作。

Answers:


15

GNU Parallel为此而设计。

seq 1 1000 | parallel -j20 do_something

它甚至可以在远程计算机上运行作业。以下是使用server2和每个CPU内核运行1个作业的本地计算机将MP3重新编码为OGG的示例:

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

在此处观看GNU Parallel的介绍性视频:

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


尚不了解“ moreutils”,并且已经有完成这项工作的工具。寻找和比较。
六。

1
parallel在moreutils不是GNU并行,并在其选项是非常有限的。上面的命令将无法与moreutils的并行文件一起运行。
Ole Tange

1
另一种选择:xargs --max-procs=20
六。

4

不是bash解决方案,但是您应该使用Makefile,可能的大小-l不要超过某些最大负载。

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

然后一次开始20个工作

$ make -j20

或在不超过5个负载的情况下开始尽可能多的工作

$ make -j -l5

看起来现在是非hacky解决方案。
六。

2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20现在看起来又变了。
六。

@vi:哦,我的....
本杰明·班尼尔

将您的解决方案转换为脚本。现在可以轻松使用。
六。

2

使用格式将脚本发布到问题中:

#!/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 ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

请注意,必须在“ i =”之前用2个制表符替换8个空格。


1

一个简单的想法:

在do_something之前检查i模20并执行wait shell命令。


它要么等待所有当前任务完成(在任务数量图中创建下陷),要么等待一个可能停滞更长时间的特定任务(在这种情况下再次创建下陷)
Vi。

@Vi:Shell等待用于属于此Shell的所有后台任务。
harrymc 2010年

1

你可以用 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
for i in {1..1000}; 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
六。

当然,但是在我的示例中,我将不得不计算njobs两次,并且性能在运行睡眠任务的shell脚本中非常重要;)
msw 2010年

我的意思是你的版本无法正常工作。我sleep 1改为sleep 0.1,它开始将平均njobs改为40-50,而不是20。如果有20个以上的工作,我们需要等待所有工作完成,而不仅仅是等待1秒钟。
六。

0

你可以这样

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

使用命名管道,每次它并行运行20个子外壳。

希望对您有所帮助:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.