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

如何限制bash中函数中使用的线程/子进程的数量,bash,Bash,我的问题是如何更改此代码,使其仅使用4个线程/子进程 TESTS="a b c d e" for f in $TESTS; do t=$[ ( $RANDOM % 5 ) + 1 ] sleep $t && echo $f $t & done wait 您可以使用作业内置: for f in $TESTS; do running=($(jobs -rp)) while [ ${#running[@]} -ge 4 ] ; do sleep 1

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

TESTS="a b c d e"

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

您可以使用
作业
内置:

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

这个经过测试的脚本一次运行5个作业,并将尽快重新启动一个新作业(因为当我们得到一个SIGCHLD时,sleep 10.9被杀死了。更简单的版本可以使用直接轮询(将sleep 10.9更改为sleep 1,并摆脱陷阱)

!/usr/bin/bash
set-o监视器
陷阱“pkill-P$$-f'sleep 10\.9'>&/dev/null”SIGCHLD
总工作=15
numjobs=5
工作时间=10
curjobs=0
声明-pidlist
dojob()
{
插槽=$1
时间=$(回声“$RANDOM*10/32768”| bc-l)
使用args$time回显开始作业$slot
睡眠时间&
pidlist[$slot]=`jobs-p%%`
curjobs=$($curjobs+1))
totaljobs=$($totaljobs-1))
}
#开始
而[$curjobs-lt$numjobs-a$totaljobs-gt 0]
做
dojob$curjobs
完成
#投票决定是否停止工作,并在我们有工作时重新启动
而[$totaljobs-gt 0]
做
对于((i=0;$i<$curjobs;i++)
做
if!kill-0${pidlist[$i]}>&/dev/null
然后
dojob$i
打破
fi
完成
睡眠10.9>&/dev/null
完成
等待

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

快速而肮脏的解决方案:将此行插入
for
循环中的某个位置:

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

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

我使用
并行
(属于
moreutils
包的一部分)找到了解决此问题的另一个方法

-j4
代表
-jmaxjobs

-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

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
观看介绍视频了解更多信息:


这些不是线程,而是子过程。你能考虑接受一个答案,并在你的问题中改变“线程”到“子进程”吗?用错误的词使得在Web上更难找到!!)-这可能是一个非常有效率的<代码>作业-r>代码>没有它,您将计算bash告诉您哪些作业已经完成的行数。这个解决方案非常好。我只是把线插在正确的地方,砰的一声!我喜欢我根本不需要改变我的脚本结构
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
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