如何使用并发和等待限制执行多个命令?

时间:2016-11-29 20:27:47

标签: linux bash concurrency parallel-processing gnu-parallel

我想要达到的目标是:

#!/bin/sh
concurrency_limit 3

#takes 5 min
(/usr/bin/my-process-1 --args1 && /usr/bin/my-process-2 --args1) & 
#takes 10 min
(/usr/bin/my-process-1 --args2 && /usr/bin/my-process-2 --args2) &
#takes 15 min
(/usr/bin/my-process-1 --args3 && /usr/bin/my-process-2 --args3) &
#takes 5 min
(/usr/bin/my-process-1 --args4 && /usr/bin/my-process-2 --args4) &
#takes 10 min
(/usr/bin/my-process-1 --args5 && /usr/bin/my-process-2 --args5) &
#takes 20 min
(/usr/bin/my-process-1 --args6 && /usr/bin/my-process-2 --args6) &

wait max_limit 1200
echo all processes complete

总体预期最长执行时间为20分钟( - + 1分钟),假设我有3个cpu核心可用,并且我不希望同时运行3个以上的进程。 / p>

在脚本开始时,前3个进程开始。

5分钟后:第一个过程结束,第四个过程开始。

第10分钟:第2和第4个过程结束,第5个过程开始。

第15分钟:第3个过程结束。

第20分钟:第5个过程结束。第6个过程在没有进一步等待的情况下被杀死。

我在stackoverflow上做了很多研究,但我找不到类似的用例:

How to wait in bash for several subprocesses to finish and return exit code !=0 when any subprocess ends with code !=0?

https://www.codeword.xyz/2015/09/02/three-ways-to-script-processes-in-parallel/

http://www.gnu.org/software/parallel/

感谢任何帮助或评论。

3 个答案:

答案 0 :(得分:2)

除非我错过了什么,否则我认为 GNU Parallel 会很容易为你做到这一点。

如果您创建一个名为jobs的文件,其中包含:

./my-process-1 --args1 && ./my-process-2 --args1
./my-process-1 --args2 && ./my-process-2 --args2
./my-process-1 --args3 && ./my-process-2 --args3
./my-process-1 --args4 && ./my-process-2 --args4
./my-process-1 --args5 && ./my-process-2 --args5
./my-process-1 --args6 && ./my-process-2 --args6

然后你可以看到 GNU Parallel 将使用--dry-run做什么,如下所示:

parallel --dry-run -j 3 -k -a jobs

<强>输出

./my-process-1 --args1 && ./my-process-2 --args1
./my-process-1 --args2 && ./my-process-2 --args2
./my-process-1 --args3 && ./my-process-2 --args3
./my-process-1 --args4 && ./my-process-2 --args4
./my-process-1 --args5 && ./my-process-2 --args5
./my-process-1 --args6 && ./my-process-2 --args6

如果my-process-1需要3秒而my-process-2需要5秒,则整数需要16秒,因为前3行是并行执行的,每行需要8秒,然后接下来的3行是并行执行并再花8秒。

答案 1 :(得分:1)

你可以用xargs做到这一点。例如,下面将使用3个并行进程为参数3,3,4,1,4和15运行函数“func”6次,并在10秒后终止它:

function func  { echo args:$1; sleep $1; echo done; }
export -f func

function worktodo { echo -e 3\\n 3\\n 4\\n 1\\n 4\\n 15 | xargs -P 3 -I {} sh -c 'func "$@"' _ {}; }
export -f worktodo

timeout 10 sh -c "worktodo" || echo "timeout"

答案 2 :(得分:1)

这是一个骨架,使用SIGINT在父级和子进程之间进行通信。

设置一个陷阱来计算忙碌的进程数量,当一个进程结束时,启动另一个进程:

trap '{ let Trapped++; }' INT  # start another child

将其初始化为您希望并行运行的数量:

Trapped=$ATONCE  # 3 in your case

然后根据需要循环并启动子项:

while true
do
  # Assuming there's more work to do. You need to decide when to terminate
  do_work &

  while [ $Trapped -le 0 ]
      wait         # race condition, interruptible by SIGINT
      local rc=$?  # ...
  done
done

然后在do_work中,您需要以下内容:

call-external-process with parms

# Deal with problems
[[ $? -ne 0 ]] && { .... }

# Now tell parent we're done
kill -INT $$

这是一个粗略的想法。缺少的是你如何知道什么时候你没有更多的流程可以启动,它需要更好的错误处理,但希望你能得到这个想法。将会有3个进程一直在运行,一个进程在结束时启动,直到没有什么可做的。