shell中没有多进程的概念,可以通过开启子shell并在后台执行来实现并发。
串行执行
#!/bin/bash
start=`date +"%s"`
for (( i=0; i<10; i++ ))
do
{
echo "execute"
sleep 1
}
done
end=`date +"%s"`
echo "time: " `expr $end - $start`
12
1
2
start=`date +"%s"`
3
for (( i=0; i<10; i++ ))
4
do
5
{
6
echo "execute"
7
sleep 1
8
}
9
done
10
11
end=`date +"%s"`
12
echo "time: " `expr $end - $start`
执行时间为10秒
并发执行
让for循环中的代码在后台子shell中执行,只需在for循环的结尾加上&,并且在for循环外加上wait语句,等待子进程结束即可。
#!/bin/bash
start=`date +"%s"`
for (( i=0; i<10; i++ ))
do
{
echo "execute"
sleep 1
}&
done
wait
end=`date +"%s"`
echo "time: " `expr $end - $start`
12
1
2
start=`date +"%s"`
3
for (( i=0; i<10; i++ ))
4
do
5
{
6
echo "execute"
7
sleep 1
8
}&
9
done
10
wait
11
end=`date +"%s"`
12
echo "time: " `expr $end - $start`
执行时间为1秒,速度提升了10倍。
这种方式比较简单,但是有个弊端,无法控制子进程的数量,如果循环一万次,会产生一万个子进程,造成不可预期的情况。
可以通过命名管道来控制子进程的数量
管道可以用于进程间通信,一个进程向管道中写入数据,同时另一个进程从管道中读取数据,管道为空进程会被阻塞,只有一个进程读或者一个进程写管道时,进程也会被阻塞。
通常使用的 cat | grep name 中的 | 是无名管道。
利用命令管道控制并发数量的实例
#!/bin/bash
fd_fifo=/tmp/fd_1
mkfifo $fd_fifo #创建命令管道(pipe类型文件)
exec 6<>$fd_fifo #将管道的fd与6号fd绑定
proc_num=5 #进程个数
count=0;
#预分配资源
for ((i=0;i<$proc_num;i++))
do
echo >& 6 #写入一个空行
done
start=`date +"%s"
for (( i=0; i<10; i++ ))
do
read -u 6 #读取一个空行
{
echo "execute"
sleep 1
echo >& 6 #完成任务,写入一个空行
}& #后台执行
done
wait #等待所有的任务完成
exec 6>&- #关闭fd 6描述符,stdou和stdin
exec 6<&-
rm -f $fifo #删除管道
end=`date +"%s"`
echo "time: " `expr $end - $start`
x
1
2
3
fd_fifo=/tmp/fd_1
4
mkfifo $fd_fifo #创建命令管道(pipe类型文件)
5
exec 6<>$fd_fifo #将管道的fd与6号fd绑定
6
proc_num=5 #进程个数
7
count=0;
8
#预分配资源
9
for ((i=0;i<$proc_num;i++))
10
do
11
echo >& 6 #写入一个空行
12
done
13
14
start=`date +"%s"
15
for (( i=0; i<10; i++ ))
16
do
17
read -u 6 #读取一个空行
18
{
19
echo "execute"
20
sleep 1
21
echo >& 6 #完成任务,写入一个空行
22
}& #后台执行
23
done
24
wait #等待所有的任务完成
25
exec 6>&- #关闭fd 6描述符,stdou和stdin
26
exec 6<&-
27
rm -f $fifo #删除管道
28
29
end=`date +"%s"`
30
echo "time: " `expr $end - $start`
执行时间为2秒(每次有5个进程在同时执行)
初始化向管道中写入5行空字符
父shell进程执行read -u 6,会从管道中读取一行,当管道为空时read -u会阻塞,保证最多只有5个shell进程在后台执行。
子shell进程执行echo >& 6,会向管道中写入一行,父shell进程阻塞状态解除,可以继续开启子shell执行任务。
父shell进程执行完10次循环后,调用wait函数,等待子shell执行完毕,回收资源。
如果不加上wait语句,父进程会直接退出,导致子进程成为孤儿进程(孤儿进程为被pid为1的init进程接管);或者子进程提前退出,父进程未执行完但不知道子进程的退出状态,会使子进程成为僵尸进程。
父进程的变量会被复制一份到子进程中,变量在子进程中的任何修改不会影响父进程中的变量。