目录
shell 并行执行
串行改为并行
这是常规串行例子
> for i in `seq 1 10`
do
sleep 1; echo $i
done
这是一个迭代次数为10的循环,每一个循环都会等待 1 秒,执行总时长约等于 10 秒。sleep 1 会阻塞循环,只有 sleep 1 执行结果,才会进入下一循环,这是典型的串行模式
shell 提供了一种把命令提交到后台任务队列的机制,即使用 命令 & 将命令控制权交到后台并立即返回执行下个任务。
> for i in `seq 1 10`
do
sleep 1 &; echo $i
done
并行-等待模式
上面将串行循环改为并行循环的例子,并没考虑这样的情况。
> for i in `seq 1 10`
do
sleep 1 &; echo $i
done
echo "all weakup"
这个例子要求在 for 循环中的所有命令(sleep 1)都执行完之后,打印 “all weakup”。如果按照这段脚本,发现情况并不是这样的,因为 for 循环不会等待 sleep 命令执行结束后才结束,而是把命令提交给系统后自己就退出了,进而还没有1个 sleep 执行完毕之前,“all weakup” 就已经打印了。
为了达到题目要求,需要在 echo "all weakup" 命令之前,加上 wait命令,意为等待上面所有 & 作用过的后台任务执行结束后才继续往下。
> for i in `seq 1 10`
do
sleep 1 &; echo $i
done
wait
echo "all weakup"
利用命名管道来做任务队列
大致原理是创建一个 FIFO 命名管道来做为队列,先放进一定量的字符到这个管道做为信号。然后在一个 for 循环中,每循环一次,从管道中读取一个字符信号,提交一个后台任务,并往这个管道中追加一个字符信号,保持管道中的字符信号数量。
是不是很像golang 中的 chan。没有接触过命名管理、文件描述符等概念的,可能会比较难以理解下面示例中的部分细节。
_fifofile="$$.fifo"
mkfifo $_fifofile # 创建一个FIFO类型的文件
exec 6<>$_fifofile # 将文件描述符6写入 FIFO 管道, 这里6也可以是其它数字
rm $_fifofile # 删也可以,
degree=4 # 定义并行度
#根据并行度设置信号个数
#事实上是在fd6中放置了$degree个回车符
for ((i=0;i<${degree};i++));do
echo
done >&6
for i in `seq 1 20` # 循环20次
do
# 从管道中读取(消费掉)一个字符信号
# 当FD6中没有回车符时,停止,实现并行度控制
read -u6
{
sleep 1 # 实际任务的命令
echo $i
echo >&6 # 当进程结束以后,再向管道追加一个信号,保持管道中的信号总数量
} &
done
wait # 等待所有任务结束
exec 6>&- # 关闭管道