工作中有时会需要跑一些任务处理的脚本,其处理的任务相对单一,但数据量大,导致单次运行脚本的时间比较长,如果任务是互相独立并且是可以分片的,那么,并行处理无疑是更快的方式。

在 shell 中,可以使用管道,生产者-消费者模型来实现任务的并行处理。下面提供一个 shell 脚本,自定义其中的 run 函数,可以实现指定并发度的并行:

#!/bin/sh

# 定义同时执行的任务数量,相当于并发度
SUB_PROCESS_NUM=20

# 参数解析,-s: 设置SUB_PROCESS_NUM,即并发度; -h: 输出帮助信息
while getopts :s:h opt
do
    case $opt in
        s) let SUB_PROCESS_NUM=OPTARG+0 ;;
        h) echo -e "Usage\n"
           echo -e "\t$0\t-s\tCocurrnet subprocess num\n"
           echo -e "\t\t\t-h\tPrint this help info\n"
           echo -e "\tAny questions pls feel free to contact  frostmourn716@gmail.com\n"
           exit 0;;
        *) echo "unknown option: $opt" ;;
    esac
done

#准备命名管道
tmpfile="$$.fifo"
mkfifo $tmpfile
exec 6<>$tmpfile
rm -rf $tmpfile

#producer写入任务标记
for ((i=1; i<=$SUB_PROCESS_NUM; i++))
do
    echo "sub job"
done >&6

# 需要自定义的任务函数,比如这里是发送请求
run(){
    ts=`date +%y%m%d%H%I%S`
    echo -e "$ts\tprocess $1"
    curl -s -X POST --data-urlencode "content@$1" --data-urlencode "id=$RANDOM" "http://*.*.*.*:8080/test.php" >/dev/null 2>&1
}

# 具体分配执行任务的代码,以处理图片为例,遍历图片,以指定的并发度处理任务
# 每个任务执行结束之后会将标记写入管道,当管道中存在任务标记时,代码会读出并且启动下一个任务
for image in `ls ./images/*`
do
    read line
    (run "${image}" >> ./log 2>&1; echo "sub job") >&6 &
done <&6