35

有一些类似的问题,但我的问题不是“并行运行多个程序”——这可以用parallelor轻松完成xargs

我需要并行化 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循环放在外部脚本中,然后并行调用该程序,但是是否可以不将功能拆分为两个单独的程序?

4

3 回答 3

52

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[@]}"
于 2013-06-26T09:27:35.400 回答
19

编辑:请考虑Ole 的回答

您可以将代码放在单独的 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 "$@"' -- 
于 2013-06-26T01:14:43.893 回答
9

一个高效的解决方案,也可以并行运行多行命令:

for ...your_loop...; do
  if test "$(jobs | wc -l)" -ge 8; then
    wait -n
  fi

  {
    command1
    command2
    ...
  } &
done
wait

在你的情况下:

for i in "${list[@]}"
do
  for j in "${other[@]}"
  do
    if test "$(jobs | wc -l)" -ge 8; then
      wait -n
    fi

    {
      your
      commands
      here
    } &
  done
done
wait

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

这种方法的好处:

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

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

  1. 只需要bash工作。

缺点:

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