【问题标题】:How to execute multiple commands with concurrency and wait limit?如何以并发和等待限制执行多个命令?
【发布时间】:2016-11-29 20:27:47
【问题描述】:

我想要实现的是这样的:

#!/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 个进程。

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

5 分钟后:第一个过程完成,第四个过程开始。

第 10 分钟:第 2 和第 4 道工序完成,第 5 道工序开始。

第 15 分钟:第三个过程完成。

第 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/

任何帮助或评论将不胜感激。

【问题讨论】:

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


    【解决方案1】:

    除非我遗漏了什么,我认为 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 秒。

    【讨论】:

    • 感谢您的回答。但是执行的总时间限制呢?
    • timeout 120 parallel ...
    【解决方案2】:

    你可以用 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"
    

    【讨论】:

      【解决方案3】:

      这是一个骨架,使用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 个进程,一个结束时启动一个新进程,直到无事可做。

      【讨论】:

        猜你喜欢
        • 2019-05-04
        • 1970-01-01
        • 2011-09-20
        • 1970-01-01
        • 1970-01-01
        • 2018-04-03
        • 2023-03-06
        • 2020-02-08
        • 1970-01-01
        相关资源
        最近更新 更多