[shell]-(一)關于多線程的管道與描述符的運用
shell雖然執行效率差,但優點,是所用即所得,不用移植,基本上所有linux都通用,基本不用額外安裝程式。s
當然運用的好的話,效率也不會太差。比如,盡可能的減少讀寫檔案,減少無名管道“|”的使用,大量的循環盡可能用awk裡完成,還有多線程的使用。
多線程,就是讓shell同時執行多行指令。
shell裡并不能同時接收多條指令,也就是,必須逐行逐條執行,想要實作執行多行,可以把前一條指令置入背景,後面的指令就可以不必等待前一條的完畢,而是立即執行。
# 背景執行在指令行結尾加”空格&“
# while true;do sleep 5s;done &
# while true;do sleep 5s;done
^C
# while true;do sleep 5s;done &
#
這樣就可以很開心的多行執行指令了:# cat proc.sh
#!/bin/bash
for i in $(seq 5);do
echo "While A 01 : $i" >>proc_test.txt
echo "While A 02 : $i" >>proc_test.txt
echo "While A 03 : $i" >>proc_test.txt
sleep 1s
done &
for j in $(seq 5);do
echo " While B 01 : $j" >>proc_test.txt
echo " While B 02 : $j" >>proc_test.txt
echo " While B 03 : $j" >>proc_test.txt
sleep 0.5s
done &
wait
預想的是向檔案proc_test.txt每次輸入三行,而實際。。。。。
cat proc_test.txt
# sh proc.sh
# cat proc_test.txt
While A 01 : 1
While A 02 : 1
While B 02 :
While A 03 : 1
While B 03 :
While B 02 :
While B 03 :
While A 01 : 2
While A 02 : 2
While A 03 : 2
While B 02 :
While B 03 :
While B 02 :
While B 03 :
While A 01 : 3
While A 02 : 3
While A 03 : 3
While B 02 :
While B 03 :
While A 01 : 4
While A 02 : 4
While A 03 : 4
While A 01 : 5
While A 02 : 5
While A 03 : 5
#
不但行是亂的,列也出現了混亂。。出現這種情況,是因為在while A輸出時,while B也在輸出,當對同一個檔案寫入時,A B的内容就被混編寫入了檔案。
這種混亂的多線程,并不是我們想要的。
必須得讓他們有順序的輸出。
管道!這時就該他發揮作用了。
管道就像名字一樣,是一個運載資料流的通道,可以像水管一樣控制它的開關和流速。
mkfifo用于建立管道檔案。
# mkfifo t.fifo
# ls -l t.fifo
prw-r--r-- 1 root root 0 Nov 13 15:44 t.fifo
#
建立個名為 t.fifo的管道檔案
管道可以像普通檔案一樣的讀寫操作,但是有容量限制,一般是1024位元組
向管道寫入時,必須同時讀取,否則,寫入端處于停滞狀态。反之也是,
從管道讀取時,如果管道内沒有資料,讀取端也會處于停滞狀态。
這裡就是關鍵。看到”停滞“了嗎,如果當while A寫入檔案時,讓其它while寫入這個檔案的指令行處于停滞,那麼A寫入的内容就不會被B打亂了。
這樣基本的概念就實作了。
因為管道檔案讀寫必須同步,是以這裡用到描述符
exec 描述符<>管道
<表示讀操作,>表示寫操作
echo >&100 ,向100裡輸入一行,代表一個執行令牌
read <&100 ,從100裡讀取一個令牌,也可用read -u100,如果沒有則停滞,等待新的令牌輸入
echo >&100 ,while裡這條,三行一組輸出完成時,回交令牌。
{}把多行指令分成一組
注:描述符隻在目前shell裡,或者說,目前程序及其子程序有效,
而管道檔案,是整個系統有效。是以在定義完描述符,可以 # rm t.fifo
如果讀寫不同步時
每三行一組的輸出基本實作了
#cat proc_test.txt
最後的小問題,當echo $i >>file.txt時,是處于背景,是以{}外的$i 不會影響{}裡的$i 的值。
上面已基本完成一個多線程的規範輸出,但如果whileAB都是無限循環,且背景部分要更耗時,那麼shell下的while程序就會不斷的增加,增加到無限多。最終系統承載不住,就會當機。
這并不是我們希望發生的,那麼就應該限制一下,背景的最大數量。
解決方法很多,當然你也可以用記數循環+循環的套用來實作、
在這裡,我們要用管道的讀寫特性來實作背景數量的限制。
和前面的有序輸出是同一個原理,有序輸出,每次隻能讀取一個執行令牌,也就是單車道。
那麼我們為背景程序新開設個多車道,就暫定為5道并開。
#!/bin/bash
mkfifo t.fifo # 建立管道檔案
exec 100<>t.fifo #定義描述符
# 1車道
echo >&100
mkfifo proc.fifo proc2.fifo #定義2兩管道檔案
exec 101<>proc.fifo #定義描述符
exec 102<>proc.fifo #定義描述符
# 開通5車道
echo |tee >&101 >&102
echo |tee >&101 >&102
echo |tee >&101 >&102
echo |tee >&101 >&102
echo |tee >&101 >&102
# 這裡定義個管道,用于,外部向腳本發送指令
mkfifo manager.fifo
exec 1000<manager.fifo
for i in $(seq 5);do
read <&101 #如果沒有補充,最多讀取5次,否則停滞等待
{
read <&100
echo "While A 01 : $i" >>proc_test.txt
echo "While A 02 : $i" >>proc_test.txt
echo "While A 03 : $i" >>proc_test.txt
echo >&100 # 補充1次
} &
echo >&101
sleep 1s
done &
for j in $(seq 5);do
read <&101 #如果沒有補充,最多讀取5次,否則停滞等待
{
read <&100
echo " While B 01 : $j" >>proc_test.txt
echo " While B 02 : $j" >>proc_test.txt
echo " While B 03 : $j" >>proc_test.txt
echo >&101 # 補充1次
} &
sleep 0.5s
done &
while true;do
read -u1000 manager
# 試試在腳本執行時,再開啟個終端,并echo "hello shell" > manager.fifo
# 腳本會接收到這個資訊,并列印出來。
echo -e "\t\t這裡是從腳本外得到的輸入内容 : $manager"
done
wait
如此這般,雙背景的5線程并行就設定好了
注:echo |tee >&101 >&102 相當于
echo >&101 ;echo >&102
tee 把輸出的内容分流給2個指向tee -a file是追加模式
最後附加了,從腳本外向腳本内傳送資訊。
第一次寫東西,有點亂。
下一篇 ----> 定義管道的通用腳本