如何限制函数中使用的子进程数量

问题描述 投票:0回答:8

我的问题是如何更改此代码,使其仅使用 4 个子进程?

TESTS="a b c d e"

for f in $TESTS; do
  t=$[ ( $RANDOM % 5 )  + 1 ]
  sleep $t && echo $f $t &
done
wait
bash multiprocessing
8个回答
43
投票

有趣的问题。我尝试使用 xargs 来实现此目的,并找到了一种方法。

试试这个:

seq 10 | xargs -i --max-procs=4 bash -c "echo start {}; sleep 3; echo done {}"

--max-procs=4
将确保同时运行的子进程不超过四个。

输出将如下所示:

start 2
start 3
start 1
start 4
done 2
done 3
done 1
done 4
start 6
start 5
start 7
start 8
done 6
done 5
start 9
done 8
done 7
start 10
done 9
done 10

请注意,执行顺序可能不会按照您提交命令的顺序执行。正如您所看到的,2 在 1 之前开始。


21
投票

快速而肮脏的解决方案:将此行插入到

for
循环中的某个位置:

while [ $(jobs | wc -l) -ge 4 ] ; do sleep 1 ; done

(假设您还没有在同一个 shell 中运行其他后台作业)


12
投票

我使用

parallel
moreutils
包的一部分)找到了这个问题的另一个解决方案。)

parallel -j 4 -i bash -c "echo start {}; sleep 2; echo done {};" -- $(seq 10)

-j 4
代表
-j maxjobs

-i
使用参数为 {}

--
界定你的论点

该命令的输出将是:

start 3
start 4
start 1
start 2
done 4
done 2
done 3
done 1
start 5
start 6
start 7
start 8
done 5
done 6
start 9
done 7
start 10
done 8
done 9
done 10

7
投票

GNU Parallel 专为此类任务而设计:

TESTS="a b c d e"
for f in $TESTS; do
  t=$[ ( $RANDOM % 5 )  + 1 ]
  sem -j4 sleep $t && echo $f $t
done
sem --wait

观看介绍视频以了解更多信息:

http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


7
投票

您可以使用内置的

jobs
来执行类似的操作:

for f in $TESTS; do
  running=($(jobs -rp))
  while [ ${#running[@]} -ge 4 ] ; do
    sleep 1   # this is not optimal, but you can't use wait here
    running=($(jobs -rp))
  done
  t=$[ ( $RANDOM % 5 )  + 1 ]
  sleep $t && echo $f $t &
done
wait

0
投票

这个经过测试的脚本一次运行 5 个作业,并且一旦完成就会重新启动一个新作业(由于当我们收到 SIGCHLD 时睡眠 10.9 被终止。一个更简单的版本可以使用直接轮询(更改睡眠 10.9睡觉 1 并摆脱陷阱)。

#!/usr/bin/bash

set -o monitor
trap "pkill -P $$ -f 'sleep 10\.9' >&/dev/null" SIGCHLD

totaljobs=15
numjobs=5
worktime=10
curjobs=0
declare -A pidlist

dojob()
{
  slot=$1
  time=$(echo "$RANDOM * 10 / 32768" | bc -l)
  echo Starting job $slot with args $time
  sleep $time &
  pidlist[$slot]=`jobs -p %%`
  curjobs=$(($curjobs + 1))
  totaljobs=$(($totaljobs - 1))
}

# start
while [ $curjobs -lt $numjobs -a $totaljobs -gt 0 ]
 do
  dojob $curjobs
 done

# Poll for jobs to die, restarting while we have them
while [ $totaljobs -gt 0 ]
 do
  for ((i=0;$i < $curjobs;i++))
   do
    if ! kill -0 ${pidlist[$i]} >&/dev/null
     then
      dojob $i
      break
     fi
   done
   sleep 10.9 >&/dev/null
 done
wait

0
投票

这是我在 AIX 上使用 bash 的“并行”解压循环:

for z in *.zip ; do
  7za x $z >/dev/null
  while [ $(jobs -p|wc -l) -ge 4 ] ; do
    wait -n
  done
done

备注:

  • jobs -p(bash 函数)列出直接父级的作业
  • wait -n(bash 函数)等待任何(一个)后台进程 完成

0
投票

一个通用的答案,当存在大量(不是太多)长时间运行的作业并且仅使用 POSIX shell 功能和文件系统时很有用。

  1. 将所有作业作为脚本放入作业目录中。
  2. 运行多个作业处理器来处理它们。

这是一个非常简单的作业处理器,将其安装在名称

queue
下:

#!/bin/sh

queue () {
    ID="$1" JOBS="$2"
    for JOB in "$JOBS"/*.job; do
    mv "$JOB" "$JOB.$ID" 2>/dev/null || continue
    sh "$JOB.$ID" && rm "$JOB.$ID"
    done
}

for JOB in $(seq $1); do  queue $JOB $2 &  done

queue 4 workdir
为目录
workdir
启动四个作业处理器。

回答问题:

创建五个工作岗位:

for f in a b c d e; do
    t=$((RANDOM % 5 + 1))
    echo "sleep $t
    echo $f $t" > workdir/job$((I+=1)).job
done

在四个子进程中处理它们并等待

queue 4 workdir; wait

如果作业失败,它将被留在

workdir/jobM.job.N
中以供检查或重复。
M
是作业的编号,
N
是处理该作业的队列的编号。

© www.soinside.com 2019 - 2024. All rights reserved.