本随笔主要讲述在shell编程中实现任务并发处理。


一、调度脚本

#!/bin/sh

help()
{
    echo "使用说明:"
    echo " $0 子进程脚本 [slots]"

    exit
}

if [ $# -lt 1 ]; then help; fi

#总任务数量
nJobs=4671
nSlots=${2:-8}
#设定工作目录
WORK_PATH=`pwd`
#设定工作脚本
work_script=$1
#actor下载地址
URL_ACTOR="https://www.meituri.com/t"
URL=$URL_ACTOR
#中断退出标志变量
exit_flag=0
#临时测试变量
flag=0
flag_set=1

exit_generate(){
	if [ $exit_flag -eq 1 ]; then
		echo "检测到重复操作,强制退出,会丢失保存数据"
                exit 1
	fi
	exit_flag=1
	echo "保存配置文件"
	echo "flag=$flag flag_set=$flag_set cjob=$cjob nJobs=$nJobs" > config.ini
    echo "等待子线程结束..."
    while [ ${finish} -eq 0 ]; do
        finish=1
        idx=1
        while [ ${idx} -le ${nSlots} ]; do
            if [ -f $$_lock_${idx}.lck ]; then
                finish=0
                jobsDump ${idx}
            else
                rm -f "$$_dump_${idx}.dmp"
                jobsDump ${idx} "本线程任务已结束..."
            fi
        idx=$((idx + 1))
        done
	sleep 1
    done
    echo "所有子线程已经全部结束,正常退出!"
	exit 1
}

exit_2(){
	echo "捕获中断信号:SIGINT"
	exit_generate
}

exit_15(){
	echo "捕获中断信号:SIGTERM"
	exit_generate
}

#设置中断捕获响应
trap exit_2 2
trap exit_15 15
jobsInit()
{
    clear
    echo "$1"", ${nSlots} downloader"

    idx=1
    while [ $idx -le ${nSlots} ]; do
        printf "\033[01;34mSlot %2d:\033[00m " $idx
        idx=$((idx + 1))
    done
}

jobsDump()
{
    printf "\033[01;34mSlot %2d:\033[00m `date +%H:%M:%S` " $1

    if [ -f $$_dump_$1.dmp ]; then
        echo "`head -n1 $$_dump_$1.dmp`"
    elif [ v"$2" != "v" ]; then
        echo "$2"
    fi
}

jobsInfo()
{
    echo $*
}
if [ -f config.ini ]; then
    echo "读取配置文件,恢复上一次的下载记录"
	. ./config.ini
else
	# clean dir	
	rm -rf Downloads
	rm -f weblinklist.txt
    cjob=0
fi
# Process jobs

finish=0
jobs_name="任务"

while [ ${finish} -eq 0 ]; do
    jobsInfo "正在处理 ${cjob}/${nJobs} $jobs_name..."
    finish=1
    idx=1
    while [ ${idx} -le ${nSlots} ]; do
        if [ -f $$_lock_${idx}.lck ]; then
            finish=0
            jobsDump ${idx}
        else
            rm -f "$$_lock_${idx}.lck" "$$_dump_${idx}.dmp"
            jobsDump ${idx} "空闲."

            if [ ${cjob} -lt ${nJobs} ]; then
                finish=0
          #这里调度工作脚本,可以在这前面写逻辑生成调度脚本的参数

          #这里通过新开shell来执行,防止调度脚本被中断后工作脚本一同遭殃

		sh $work_script $$ ${idx} "$$_lock_${idx}.lck" "$$_dump_${idx}.dmp" $WORK_PATH $flag $URL $flag_set &
                #创建/更新目录锁文件
                #touch "$$_lock_${idx}.lck";
                # Dump process info
                jobsDump ${idx} "已分配任务,工作中..."
                cjob=$((cjob + 1))
			
            fi
        fi
        idx=$((idx + 1))
    done
	sleep 1
done

echo "所有任务已完成"

#删除创建的所有临时文件
rm -f $$_*

 调度脚本主要负责调度用于执行真正工作任务的工作脚本,同时承担向工作脚本传不同的参数来向工作脚本下发不同的任务。

本脚本在原有参考的基础上进行了改进,新增中断捕获(两次中断强制退出),可以在结束任务的时候依旧保证各工作脚本的工作不被打断,同时监控各工作脚本的运行情况,此时不会再下发新任务,同时会保存当前的工作记录,可用于下一次启动的恢复上次工作的位置;修改了锁文件创建的逻辑,由被调度脚本创建并自行销毁,解决捕获中断可能造成的锁文件存在但工作任务未被调度的情况。

二、工作脚本

#!/bin/sh

# dowork.sh
# 调用方法和参数总结
# 该脚本被multi.sh调用执行,传递给脚本7-8个参数
# param 1: PID of multi.sh
# param 2: slot number
# param 3: lock file name
# param 4: info file name
# param 5: working dir
# param 6: working flag (set 0 to actorParser, set 1 to downloadParser)


#创建线程锁文件,标识线程运行态
touch "$3";
#配置文件
#wget下载参数配置
#重试次数:5,超时时间:5
tries=5
timeout=5
#宏参量定义
output=`pwd`/$4

#配置文件结束

#主程序起始点
#向output输出的信息可以被调度脚本所打印
echo "子进程接受下发任务,准备处理,工作标志flag=$6" > $output


# 在这上面可以写该脚本需要完成命令
echo "$2当前任务执行完毕,退出" > $output
# 脚本运行完了,在这里将锁文件删除,multi.sh就可以知道该脚本执行完毕了
rm -f $3

 该脚本用于接受来自调度脚本下发的任务并运行,通过创建的info文件向调度脚本实时汇报自身工作状态。