如何限制bash中函数中使用的线程/子进程数

时间:2011-06-28 19:13:30

标签: bash

我的问题是如何更改此代码,以便它只使用4个线程/子进程?

TESTS="a b c d e"

for f in $TESTS; do
  t=$[ ( $RANDOM % 5 )  + 1 ]
  sleep $t && echo $f $t &
done
wait

6 个答案:

答案 0 :(得分:35)

有趣的问题。我尝试使用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之前。

答案 1 :(得分:13)

快速而肮脏的解决方案:在for循环内的某处插入此行:

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

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

答案 2 :(得分:8)

我使用parallelmoreutils包的一部分)找到了此问题的另一种解决方案。)

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

答案 3 :(得分:5)

您可以使用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

答案 4 :(得分:3)

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

答案 5 :(得分: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