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
…四次,前三次调用将立即返回,第四次调用将阻塞,直到运行的作业少于三个

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


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

一个小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 请参见

您可以使用ulimit-u
请参见

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

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


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

如果您愿意在纯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}']
    完成
    
    这是一个运行示例,插入了空白行以清楚地描绘不同的时间
    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;