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進程接管);或者子進程提前退出,
父進程未執行完但不知道子進程的退出狀態,會使子進程成為僵屍進程。
父進程的變量會被復制一份到子進程中,變量在子進程中的任何修改不會影響父進程中的變量。
