Bash:限制并发作业的数量?

Bash:限制并发作业的数量?,bash,shell,concurrency,Bash,Shell,Concurrency,有没有一种简单的方法来限制bash中并发作业的数量?我的意思是在后台运行超过n个并发作业时生成&块 我知道我可以用ps | grep风格的技巧实现这一点,但有没有更简单的方法?一个小的bash脚本可以帮助您: # content of script exec-async.sh joblist=($(jobs -p)) while (( ${#joblist[*]} >= 3 )) do sleep 1 joblist=($(jobs -p)) done $* &

有没有一种简单的方法来限制bash中并发作业的数量?我的意思是在后台运行超过n个并发作业时生成&块


我知道我可以用ps | grep风格的技巧实现这一点,但有没有更简单的方法?

一个小的bash脚本可以帮助您:

# content of script exec-async.sh
joblist=($(jobs -p))
while (( ${#joblist[*]} >= 3 ))
do
    sleep 1
    joblist=($(jobs -p))
done
$* &
如果你打电话:

. exec-async.sh sleep 10
…四次,前三次调用将立即返回,第四次调用将阻塞,直到运行的作业少于三个

您需要在当前会话中以
作为前缀启动此脚本,因为
作业
仅列出当前会话的作业


内部的
睡眠
很难看,但我没有找到等待第一个作业终止的方法。

您是否考虑过启动十个长时间运行的侦听器进程并通过命名管道与它们通信?

您可以使用ulimit-u
请参见

如果您愿意在纯bash之外执行此操作,则应该研究作业排队系统

例如,有或。对于PBS,您可能需要查看配置


这两个系统都需要一些配置,但完全可以允许同时运行特定数量的作业,只有在运行的作业完成时才启动新排队的作业。通常,这些作业队列系统将用于超级计算集群,在超级计算集群中,您希望为任何给定的批处理作业分配特定数量的内存或计算时间;但是,没有理由不考虑计算时间或内存限制就不能在一台台式计算机上使用这些函数中的一个。

下面的脚本展示了一种使用函数实现这一点的方法。您可以将
bgxupdate()
bgxlimit()
函数放在脚本中,也可以将它们放在一个单独的文件中,该文件源于您的脚本,其中包含:

/路径/to/bgx.sh
它的优点是,您可以独立维护多个进程组(例如,您可以运行一个限制为
10
的组和另一个限制为
3
的完全独立的组)

它使用Bash内置的
作业
来获取子进程列表,但在单个变量中维护它们。在底部的循环中,您可以看到如何调用
bgxlimit()
函数:

  • 设置一个空的组变量
  • 将其传送到
    bgxgrp
  • 使用要运行的限制和命令调用
    bgxlimit()
  • 将新组传输回您的组变量
  • 当然,如果您只有一个组,只需直接使用
    bgxgrp
    变量,而不必进行输入和输出

    #/bin/bash
    #bgxupdate-更新组中的活动进程。
    #通过将每个流程转移到新组来工作
    #如果它仍然处于活动状态。
    #in:bgxgrp-当前进程组。
    #输出:bgxgrp-新的进程组。
    #out:bgxcount—新组中的进程数。
    bgxupdate(){
    bgxoldgrp=${bgxgrp}
    bgxgrp=“”
    ((bgxcount=0))
    bgxjobs=“$(jobs-pr | tr'\n'')”
    对于${bgxoldgrp};do中的bgxpid
    echo“${bgxjobs}”| grep“${bgxpid}”>/dev/null 2>&1
    如果[[$?-eq 0]];则
    bgxgrp=“${bgxgrp}${bgxpid}”
    ((bgxcount++)
    fi
    完成
    }
    #bgxlimit-启动具有限制的子进程。
    #循环,调用bgxupdate,直到有空闲
    #插槽以运行另一个子进程。然后运行它
    #更新进程组。
    #in:$1——进程限制。
    #in:$2+—为新进程运行的命令。
    #in:bgxgrp-当前进程组。
    #输出:bgxgrp-新流程组
    bgxlimit(){
    bgxmax=$1;班次
    bgxupdate
    而[[${bgxcount}-ge${bgxmax}]];do
    睡眠1
    bgxupdate
    完成
    如果[[“$1”!=“-”];则
    $* &
    bgxgrp=“${bgxgrp}$!”
    fi
    }
    #测试程序,创建组并与一起运行6个睡眠
    #限制为3。
    group1=“”
    echo 0$(日期| awk'{print$4}')'['${group1}']
    回声
    我在123456;做
    bgxgrp=${group1};bgxlimit3睡眠${i}0;group1=${bgxgrp}
    echo${i}$(日期| awk'{print$4}')'['${group1}']
    完成
    #等到所有其他的都完成。
    回声
    bgxgrp=${group1};bgxupdate;group1=${bgxgrp}
    而[${bgxcount}-ne 0]];做
    oldcount=${bgxcount}
    而[[${oldcount}-eq${bgxcount}]];做
    睡眠1
    bgxgrp=${group1};bgxupdate;group1=${bgxgrp}
    完成
    echo 9$(日期| awk'{print$4}')['${group1}']
    完成
    
    下面是一个运行示例,插入了空白行以清晰地描绘不同的时间点:

    012:38:00[]
    1 12:38:00 [ 3368 ]
    2 12:38:00 [ 3368 5880 ]
    3 12:38:00 [ 3368 5880 2524 ]
    4 12:38:10 [ 5880 2524 1560 ]
    5 12:38:20 [ 2524 1560 5032 ]
    6 12:38:30 [ 1560 5032 5212 ]
    9 12:38:50 [ 5032 5212 ]
    9 12:39:10 [ 5212 ]
    9 12:39:30 [ ]
    
    • 整个过程从
      12:38:00
      (time
      t=0
      )开始,如您所见,前三个进程立即运行
    • 每个进程睡眠时间
      10n
      秒,直到第一个进程退出(时间
      t),第四个进程才开始 = 10
      )。您可以看到,在添加
      1560
      之前,进程
      3368
      已从列表中消失
    • 类似地,第五个过程
      5032
      在时间
      t退出时开始
      5880
      (第二个) = 20
    • 最后,当
      2524
      (第三个)在时间
      t退出时,第六个过程
      5212
      开始 = 30
    • 然后运行开始,第四个进程在时间
      t退出 = 50
      (从
      10
      开始,持续时间
      40
    • 第五个在时间
      t退出 = 70
      (从
      20
      开始,持续时间
      50
    • 最后,第六个在时间
      t退出 = 90
      (开始于
      30
      60#!/bin/bash
      
      n=0
      maxjobs=10
      
      for i in *.m4a ; do
          # ( DO SOMETHING ) &
      
          # limit jobs
          if (( $(($((++n)) % $maxjobs)) == 0 )) ; then
              wait # wait until all have finished (not optimal, but most times good enough)
              echo $n wait
          fi
      done
      
      parallel gzip ::: *.log
      
      for i in *.log ; do
          echo $i Do more stuff here
          sem -j+0 gzip $i ";" echo done
      done
      sem --wait
      
      $ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
         fetch -o - http://pi.dk/3 ) > install.sh
      $ sha1sum install.sh | grep 67bd7bc7dc20aff99eb8f1266574dadb
      12345678 67bd7bc7 dc20aff9 9eb8f126 6574dadb
      $ md5sum install.sh | grep b7a15cdbb07fb6e11b0338577bc1780f
      b7a15cdb b07fb6e1 1b033857 7bc1780f
      $ sha512sum install.sh | grep 186000b62b66969d7506ca4f885e0c80e02a22444
      6f25960b d4b90cf6 ba5b76de c1acdf39 f3d24249 72930394 a4164351 93a7668d
      21ff9839 6f920be5 186000b6 2b66969d 7506ca4f 885e0c80 e02a2244 40e8a43f
      $ bash install.sh
      
      for x in $(seq 1 100); do     # 100 things we want to put into the background.
          max_bg_procs 5            # Define the limit. See below.
          your_intensive_job &
      done
      
      function max_bg_procs {
          if [[ $# -eq 0 ]] ; then
                  echo "Usage: max_bg_procs NUM_PROCS.  Will wait until the number of background (&)"
                  echo "           bash processes (as determined by 'jobs -pr') falls below NUM_PROCS"
                  return
          fi
          local max_number=$((0 + ${1:-0}))
          while true; do
                  local current_number=$(jobs -pr | wc -l)
                  if [[ $current_number -lt $max_number ]]; then
                          break
                  fi
                  sleep 1
          done
      }
      
      [ "$CPU_NUMBER" ] || CPU_NUMBER="`nproc 2>/dev/null || echo 1`"
      
      while [ "$1" ]; do
          {
              do something
              with $1
              in parallel
      
              echo "[$# items left] $1 done"
          } &
      
          while true; do
              # load the PIDs of all child processes to the array
              joblist=(`jobs -p`)
              if [ ${#joblist[*]} -ge "$CPU_NUMBER" ]; then
                  # when the job limit is reached, wait for *single* job to finish
                  wait -n
              else
                  # stop checking when we're below the limit
                  break
              fi
          done
          # it's great we executed zero external commands to check!
      
          shift
      done
      
      # wait for all currently active child processes
      wait
      
      waitforjobs() {
          while test $(jobs -p | wc -w) -ge "$1"; do wait -n; done
      }
      
      waitforjobs 10
      run_another_job &
      
      job_limit () {
          # Test for single positive integer input
          if (( $# == 1 )) && [[ $1 =~ ^[1-9][0-9]*$ ]]
          then
      
              # Check number of running jobs
              joblist=($(jobs -rp))
              while (( ${#joblist[*]} >= $1 ))
              do
      
                  # Wait for any job to finish
                  command='wait '${joblist[0]}
                  for job in ${joblist[@]:1}
                  do
                      command+=' || wait '$job
                  done
                  eval $command
                  joblist=($(jobs -rp))
              done
         fi
      }
      
      while :
      do
          task &
          job_limit `nproc`
      done
      
      # $1 = maximum concurent jobs
      #
      limit_jobs()
      {
         while true; do
            if [ "$(jobs -p | wc -l)" -lt "$1" ]; then break; fi
            usleep 100000
         done
      }
      
      # and now start some tasks:
      
      task &
      limit_jobs 2
      task &
      limit_jobs 2
      task &
      limit_jobs 2
      task &
      limit_jobs 2
      wait
      
      mkdir tmp ; pushd tmp ; split -l 50 ../mainfile.txt
      for file in * ; do 
         while read a b c ; do curl -s http://$a/$b/$c <$file &
         done ; wait ; done
      popd ; rm -rf tmp;