在bash中并行运行有限数量的子进程?
我有一大组文件,需要进行一些繁重的处理。 这个处理是单线程的,使用几百个MiB的RAM(在用于启动作业的机器上),运行几分钟。 我当前的用例是在输入数据上启动hadoop作业,但我以前在其他情况下也遇到过同样的问题 为了充分利用可用的CPU能力,我希望能够并行运行几个这样的任务 但是,这样一个非常简单的示例shell脚本会由于过度的负载和交换而破坏系统性能:在bash中并行运行有限数量的子进程?,bash,parallel-processing,make,xargs,Bash,Parallel Processing,Make,Xargs,我有一大组文件,需要进行一些繁重的处理。 这个处理是单线程的,使用几百个MiB的RAM(在用于启动作业的机器上),运行几分钟。 我当前的用例是在输入数据上启动hadoop作业,但我以前在其他情况下也遇到过同样的问题 为了充分利用可用的CPU能力,我希望能够并行运行几个这样的任务 但是,这样一个非常简单的示例shell脚本会由于过度的负载和交换而破坏系统性能: find . -type f | while read name ; do some_heavy_processing_comm
find . -type f | while read name ;
do
some_heavy_processing_command ${name} &
done
所以我想要的基本上与“gmake-j4”的功能相似
我知道bash支持“wait”命令,但它只等待所有子进程完成。在过去,我创建了一个脚本,它执行一个“ps”命令,然后按名称将子进程grep出来(是的,我知道……很难看)
做我想做的事情最简单/最干净/最好的解决方案是什么
编辑:谢谢弗雷德里克:是的,这确实是 “xargs--max procs=4”就像一个符咒。 (所以我投票结束了我自己的问题)
弗雷德里克说得很好,沙格斯做的正是你想要的 这段代码对我来说非常有效 我注意到一个问题,脚本无法结束。 如果遇到由于max_jobs大于数组中的元素数而导致脚本无法结束的情况,脚本将永远不会退出 为了防止出现上述情况,我在“max_jobs”声明之后添加了以下内容
if [ $max_jobs -gt ${#todo_array[*]} ];
then
# there are more elements found in the array than max jobs, setting max jobs to #of array elements"
max_jobs=${#todo_array[*]}
fi
我知道我带着这个答案来参加聚会已经晚了,但我想我会发布一个替代方案,IMHO,使脚本的主体更加清晰和简单。(显然,您可以更改值2和5以适合您的场景。)
使用GNU并行,它变得更简单:
find . -type f | parallel some_heavy_processing_command {}
了解更多信息:我想我找到了一个更方便的解决方案,使用: 将其称为“test.mak”,并添加执行权限。如果调用
/test.mak
它将逐个调用一些重处理命令。但是您可以调用as/test.mak-j4
,然后它将同时运行四个子进程。此外,您还可以以更复杂的方式使用它:以/test.mak-j5-l1.5
的形式运行,这样当系统负载低于1.5时,它将最多运行5个子进程,但如果系统负载超过1.5,它将限制进程数
它比标准分发版更灵活,并且是标准分发版的一部分,而不像另一个选项:
PARALLEL_MAX=...
function start_job() {
while [ $(ps --no-headers -o pid --ppid=$$ | wc -l) -gt $PARALLEL_MAX ]; do
sleep .1 # Wait for background tasks to complete.
done
"$@" &
}
start_job some_big_command1
start_job some_big_command2
start_job some_big_command3
start_job some_big_command4
...
这里有一个非常好的函数,我用来控制bash或ksh的最大作业数。注:pgrep中的-1减去wc-l子流程
function jobmax
{
typeset -i MAXJOBS=$1
sleep .1
while (( ($(pgrep -P $$ | wc -l) - 1) >= $MAXJOBS ))
do
sleep .1
done
}
nproc=5
for i in {1..100}
do
sleep 1 &
jobmax $nproc
done
wait # Wait for the rest
这看起来像是一项工作,但我不确定它是否为xargs--max procs添加了额外的功能,我没有这样做know@Niels:我一直在使用screen
,尽管这种方式有点混乱,尤其是从另一个screen
会话开始时;)我现在理解了代码,但不得不想一想。特别是关于为什么这些将并行运行的部分(因为它们是子流程),我没有理解。我认为在代码中为这一部分添加注释也是值得的。尽管我当前的应用程序与xargs--max procs配合得很好,但我仍然认为您是“答案”,因为您的脚本在更多情况下都是可用的。谢谢,哥们,这真是太棒了!谢谢!:)在将while语法更改为:while[$(jobs | wc-l)-ge2]之后,这对我来说是有效的。请注意,该命令非常长,可能需要几行代码才能显示出来。在这种情况下,jobs-p
应该可以做到这一点。
#!/usr/bin/make -f
THIS := $(lastword $(MAKEFILE_LIST))
TARGETS := $(shell find . -name '*.sh' -type f)
.PHONY: all $(TARGETS)
all: $(TARGETS)
$(TARGETS):
some_heavy_processing_command $@
$(THIS): ; # Avoid to try to remake this makefile
PARALLEL_MAX=...
function start_job() {
while [ $(ps --no-headers -o pid --ppid=$$ | wc -l) -gt $PARALLEL_MAX ]; do
sleep .1 # Wait for background tasks to complete.
done
"$@" &
}
start_job some_big_command1
start_job some_big_command2
start_job some_big_command3
start_job some_big_command4
...
function jobmax
{
typeset -i MAXJOBS=$1
sleep .1
while (( ($(pgrep -P $$ | wc -l) - 1) >= $MAXJOBS ))
do
sleep .1
done
}
nproc=5
for i in {1..100}
do
sleep 1 &
jobmax $nproc
done
wait # Wait for the rest