很多人都問我如何寫shell腳本,如何實現同時給三臺ftp服務器上傳文件,如何同時檢測三臺服務器是否alive等,其實這就是想實現shell的併發。那麼shell併發該如何實現呢?
下面我就拿這個例子來講:
每次任務都是輸出字符“bingfa”,並停留一秒鐘,共20次。
按照正常思維,腳本應該這樣寫:
[root@station1 ~]# cat a.sh
#!/bin/bash
for((i=0;i<20;i++))
do
sleep 1
echo "bingfa"
done
[root@station1 ~]# time bash a.sh
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
real 0m20.067s
user 0m0.016s
sys 0m0.031s
[root@station1 ~]#
可以看到執行此腳本大概用了20秒。那麼使用shell併發該怎麼寫,很多人都會想到後臺程序,類似如下:
[root@station1 ~]# cat b.sh
#!/bin/bash
for((i=0;i<20;i++))
do
{
sleep 1
echo "bingfa"
}&
done
wait
[root@station1 ~]# time bash b.sh
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
bingfa
real 0m1.060s
user 0m0.005s
sys 0m0.057s
[root@station1 ~]#
這樣寫只需花大概一秒鐘,可以看到所有的任務幾乎同時執行,如果任務量非常大,系統肯定承受不了,也會影響系統中其他程序的運行,這樣就需要一個線程數量的控制。下面是我一開始寫的代碼(是有問題的):
[root@station1 ~]# cat c.sh
#!/bin/bash
exec 6<>tmpfile
echo "1\n1\n1" &>6
for((i=0;i<20;i++))
do
read -u 6
{
sleep 1
echo "$REPLY"
echo "1" 1>&6
}&
done
wait
[root@station1 ~]# time bash c.sh
111
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
real 0m1.074s
user 0m0.012s
sys 0m0.031s
[root@station1 ~]#
可以明顯看出是有問題的,我本想控制線程個數爲3,但是就算文件描述符6中爲空,也會被讀取空,然後跳過繼續下面的執行,所以使用文件描述符打開一個文件是不行的,然後我就想着使用類似管道的文件來做,下面是我的代碼:
[root@station1 ~]# cat d.sh
#!/bin/bash
mkfifo fd2
exec 9<>fd2
echo -n -e "1\n1\n1\n" 1>&9
for((i=0;i<20;i++))
do
read -u 9
{ #your process
sleep 1
echo "$REPLY"
echo -ne "1\n" 1>&9
} &
done
wait
rm -f fd2
[root@station1 ~]# time bash d.sh
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
real 0m7.075s
user 0m0.018s
sys 0m0.044s
[root@station1 ~]#
這樣就ok了,三個線程運行20個任務,7秒多點。