一、导读:
在Bash shell中,使用后台任务来模拟实现任务的“多进程化”。在不加控制的模式下,不管有多少任务,全部都在后台开辟子进程来执行。也就是说,在这种情况下,有多少任务就有多少“进程”在同时执行。
二、实例:
1、全部在前台运行:
shell> cat thread.sh
#!/bin/sh
for ((i=0;i<5;i++))
do
{
echo $i >> i.log
sleep 1
}
done
shell> time sh thread.sh
real 0m5.064s
user 0m0.003s
sys 0m0.000s
shell> cat i.log
0
1
2
3
4
我们发现在正常的情况下,所有的代码块都在前台运行,并且顺序的执行,这样子程序执行时间较久,但是从打印出来的日志可以看出,执行的顺序并没有出现错乱。
2、使用“& + wait”模拟实现多进程:
shell> cat thread.sh
#!/bin/sh
for ((i=0;i<5;i++))
do
{
echo $i >> i.log
sleep 1
}&
done
wait
shell> time sh thread.sh
real 0m1.007s
user 0m0.001s
sys 0m0.002s
shell> cat i.log
3
2
4
1
0
此时我们发现程序执行时间基本上等于执行一次循环的时间,这是因为每执行一次循环就会创建一个子进程,wait会等待所有的子进程执行完毕之后才会执行后面的代码块(这里没有写,就直接退出了)。
将上述的代码中sleep 1修改为sleep 100 重新执行,然后新开一个会话窗口查看进程状态的话,就可以更加直观的感受到上述的解释:
shell> ps -ef | head -n1; ps -ef | grep thread.sh
UID PID PPID C STIME TTY TIME CMD
root 9649 9382 0 22:26 pts/0 00:00:00 sh thread.sh
root 9650 9649 0 22:26 pts/0 00:00:00 sh thread.sh
root 9651 9649 0 22:26 pts/0 00:00:00 sh thread.sh
root 9652 9649 0 22:26 pts/0 00:00:00 sh thread.sh
root 9653 9649 0 22:26 pts/0 00:00:00 sh thread.sh
root 9654 9649 0 22:26 pts/0 00:00:00 sh thread.sh
root 9723 9662 0 22:27 pts/1 00:00:00 grep thread.sh
但是这种方式也有自己的缺点,就是它对进程的数量是不可控的:无论有多少次循环,就会创建出多少个子进程,这在循环次数巨大的程序中显然是不合适的。
3、后台进程数量可控的“多进程”:
#!/bin/bash
#———————————————————————————–
# 此例子说明了一种用wait、read命令模拟多线程的一种技巧
# 此技巧往往用于多主机检查,比如ssh登录、ping等等这种单进程比较慢而不耗费cpu的情况
# 还说明了多线程的控制
#———————————————————————————–
# 此处定义一个函数,作为一个线程(子进程)
function a_sub {
sleep 3
}
tmp_fifofile="/tmp/$$.fifo"
mkfifo $tmp_fifofile # 新建一个fifo类型的文件
exec 6<>$tmp_fifofile # 将fd6指向fifo类型
rm $tmp_fifofile
thread=15 # 此处定义线程数
for ((i=0;i<$thread;i++));do
echo
done >&6 # 事实上就是在fd6中放置了$thread个回车符
# 50次循环,可以理解为50个主机,或其他
for ((i=0;i<50;i++))
do
read -u6
# 一个read -u6命令执行一次,就从fd6中减去一个回车符,然后向下执行,
# fd6中没有回车符的时候,就停在这了,从而实现了线程数量控制
# 此处子进程开始执行,被放到后台
{
a_sub && {
echo "a_sub is finished"
} || {
echo "sub error"
}
echo >&6 # 当进程结束以后,再向fd6中加上一个回车符,即补上了read -u6减去的那个
} &
done
wait # 等待所有的后台子进程结束
exec 6>&- # 关闭fd6
exit 0
shell> time sh thread.sh > /dev/null
real 0m12.047s
user 0m0.005s
sys 0m0.007s
sleep 3s,线程数为15,一共循环50次,所以,此脚本一共的执行时间大约为12秒
即:15×3=45, 所以 3 x 3s = 9s
(50-45=5)<15, 所以 1 x 3s = 3s
所以 9s + 3s = 12s
此程序中的命令 mkfifo tmpfile和linux中的命令 mknod tmpfile p效果相同。
区别是mkfifo为POSIX标准,因此推荐使用它。该命令创建了一个先入先出的管道文件,并为其分配文件标志符6。管道文件是进程之间通信的一种方式,注意这一句很重要
exec 6<>$tmp_fifofile # 将fd6指向fifo类型
如果没有这句,在向文件$tmp_fifofile或者&6写入数据时,程序会被阻塞,直到有read读出了管道文件中的数据为止。而执行了上面这一句后就可以在程序运行期间不断向fifo类型的文件写入数据而不会阻塞,并且数据会被保存下来以供read程序读出。
暂无评论