如何在Bash中并行运行给定的函数?

时间:2013-06-25 21:24:27

标签: bash parallel-processing

有一些类似的问题,但我的问题不是“并行运行多个程序” - 这可以通过parallelxargs轻松完成。

我需要并行化Bash函数。

让我们想象一下这样的代码:

for i in "${list[@]}"
do
    for j in "${other[@]}"
    do
    # some processing in here - 20-30 lines of almost pure bash
    done
done

某些处理需要调用外部程序。

我想运行一些(4-10)任务,每个任务针对不同的$i运行。 $ list中的元素总数是> 500。

我知道我可以将整个for j ... done循环放在外部脚本中,并且只是并行调用该程序,但是可以不在两个单独的程序之间拆分功能吗?

3 个答案:

答案 0 :(得分:41)

semGNU Parallel的一部分,是针对这种情况制作的。

for i in "${list[@]}"
do
    for j in "${other[@]}"
    do
        # some processing in here - 20-30 lines of almost pure bash
        sem -j 4 dolong task
    done
done

如果您更喜欢这个函数,GNU Parallel可以一次性执行双for循环:

dowork() { 
  echo "Starting i=$1, j=$2"
  sleep 5
  echo "Done i=$1, j=$2"
}
export -f dowork

parallel dowork ::: "${list[@]}" ::: "${other[@]}"

答案 1 :(得分:15)

修改:请改为考虑Ole's answer

您可以将代码放在单独的bash函数中,而不是单独的脚本。然后,您可以将其导出,并通过xargs:

运行它
#!/bin/bash
dowork() { 
    sleep $((RANDOM % 10 + 1))
    echo "Processing i=$1, j=$2"
}
export -f dowork

for i in "${list[@]}"
do
    for j in "${other[@]}"
    do
        printf "%s\0%s\0" "$i" "$j"
    done
done | xargs -0 -n 2 -P 4 bash -c 'dowork "$@"' -- 

答案 2 :(得分:2)

并行运行多行命令的解决方案:

for ...your_loop...; do
  test "$(jobs | wc -l)" -ge 8 && wait -n || true  # wait if needed

  {
    any bash commands here
  } &
done
wait

在你的情况下:

for i in "${list[@]}"
do
    for j in "${other[@]}"
    do
        test "$(jobs | wc -l)" -ge 8 && wait -n || true
        {
            your
            multi-line
            commands
            here
        } &
    done
done
wait

如果有8个bash作业已经运行,wait将等待至少一个作业完成。如果/当作业较少时,它会异步启动新作业。

这种方法的好处:

  1. 多行命令非常容易。所有变量都在范围内自动“捕获”,无需将它们作为参数传递
  2. 它相对较快。比较这个,例如,并行(我引用官方man):
      

    启动时并行速度很慢 - 第一次约250毫秒,之后约150毫秒。

  3. 只需要bash即可。
  4. 缺点:

    1. 当我们计算它们时,有可能有8个工作岗位,但是当我们开始等待时,工作岗位可能会减少。 (如果作业在两个命令之间以毫秒结束,则会发生这种情况。)这可以使我们wait的作业少于所需的作业。但是,它将在至少一个作业完成时恢复,或者在有0个作业运行时立即恢复(在这种情况下立即退出wait -n)。
    2. 如果您已经在同一个bash脚本中异步运行某些命令(&),那么循环中的工作进程就会减少。