當(dāng)我們工作中遇到一些批量分發(fā)、批量執(zhí)行場(chǎng)景時(shí),寫的一些shell腳本都是單線程任務(wù),當(dāng)然這些量級(jí)不大的時(shí)候,看不出劣勢(shì)。
舉個(gè)例子:現(xiàn)在需要通過(guò)跳板機(jī),分發(fā)一個(gè)文件到10臺(tái)服務(wù)器,每臺(tái)服務(wù)器傳輸需要1s時(shí)間,10臺(tái)服務(wù)器傳輸完就是10s。看下面腳本,這里通過(guò)sleep命令模擬傳輸耗時(shí)。
#!/bin/bash #Author: www.zhongjima.net #Description: 模擬分發(fā)文件到10臺(tái)服務(wù)器 startTime=`date +%s` for i in `seq 10`;do echo $i sleep 1 done stopTime=`date +%s` echo "耗時(shí): $[$stopTime - $startTime] s"
這個(gè)執(zhí)行結(jié)果不用思考,就是10s。
# bash test.sh 1 2 3 4 5 6 7 8 9 10 耗時(shí): 10 s
如果是500臺(tái)服務(wù)器或者1000臺(tái)服務(wù)器,那命令執(zhí)行以后就慢慢等吧。那如果文件比較大,一個(gè)需要30s呢?1000 * 30 =30000 s 基本上就是8個(gè)多小時(shí)。早上上班執(zhí)行,等到下班剛剛好,如果你的腳本是前臺(tái)運(yùn)行,中間突然網(wǎng)絡(luò)波動(dòng),導(dǎo)致遠(yuǎn)程斷開了,那就廁所哭去吧。
其實(shí)文件分發(fā)大不了就是等嘛,但是如果是批量檢查服務(wù)器狀態(tài),或者批量啟動(dòng)服務(wù),服務(wù)器數(shù)量一大,那劣勢(shì)就特別明顯了。
那么有沒(méi)有辦法提高效率呢?答案是肯定的,利用多線程,本來(lái)需要10s完成的任務(wù),我使用了5個(gè)線程,那么跑下來(lái)基本也就2s左右。shell不像 python、go語(yǔ)言,他們本身就有多線程模塊。
shell只能利用管道和文件描述符實(shí)現(xiàn)多線程任務(wù),直接腳本測(cè)試剛才的分發(fā)文件。
#!/bin/bash #Author: www.zhongjima.net #Description: 模擬多線程分發(fā)文件到10臺(tái)服務(wù)器 startTime=`date +%s` tmpfifo="/tmp/$$.fifo" [ -e $tmpfifo ] || mkfifo $tmpfifo exec 3<>$tmpfifo rm -rf $tmpfifo for i in `seq 5`;do echo >&3 done for i in `seq 10`;do read -u3 { sleep 1 echo $i echo >&3 }& done wait stopTime=`date +%s` echo "耗時(shí): $[$stopTime - $startTime] s" exec 3<&- exec 3>&-
執(zhí)行結(jié)果如下圖:
如果是10個(gè)線程,結(jié)果就是1s,如果是6、7、8、9個(gè)線程,整個(gè)任務(wù)執(zhí)行完也是2s,所以需要根據(jù)實(shí)際的情況設(shè)置線程數(shù),也并不是線程越多越好,整個(gè)要看本身執(zhí)行腳本的服務(wù)器CPU核數(shù)以及其它資源情況。
下面對(duì)腳本語(yǔ)句進(jìn)行分析:
#!/bin/bash #Author: www.zhongjima.net #Description: 模擬多線程分發(fā)文件到10臺(tái)服務(wù)器 startTime=`date +%s` tmpfifo="/tmp/$$.fifo" #判斷有名管道是否存在 創(chuàng)建有名管道文件 [ -e $tmpfifo ] || mkfifo $tmpfifo #創(chuàng)建文件描述符 exec 3<>$tmpfifo,(可讀(<)可寫(>))創(chuàng)建文件描述符3關(guān)聯(lián)管道文件,這時(shí)候3這個(gè)文件描述符就擁有了管道的所有特性。 #還具有一個(gè)管道不具有的特性:無(wú)限存不阻塞,無(wú)限取不阻塞,而不用關(guān)心管道內(nèi)是否為空,也不用關(guān)心是否有內(nèi)容寫入引用文件描述符: &3可以執(zhí)行n次echo >&3 往管道里放入n把鑰匙。并發(fā)n exec 3<>$tmpfifo #關(guān)聯(lián)后的文件描述符擁有管道文件的所有特性,所以這時(shí)候管道文件可以刪除,留下文件描述符來(lái)用就可以了。 rm -rf $tmpfifo for i in `seq 5`;do #&3代表引用文件描述符3,這條命令代表往管道里面放入了一個(gè)"令牌",`seq 5` 循環(huán)5次,也就是5個(gè)令牌,即5個(gè)線程 echo >&3 done for i in `seq 10`;do #代表從管道中讀取一個(gè)令牌 read -u3 { sleep 1 echo $i #代表我這一次命令執(zhí)行到最后,把令牌放回管道 echo >&3 }& #用{}把循環(huán)體括起來(lái),后加一個(gè)&符號(hào),代表每次循環(huán)都把命令放入后臺(tái)運(yùn)行 done #wait命令的意思是,等待(wait命令)上面的命令(放入后臺(tái)的)都執(zhí)行完畢了再往下執(zhí)行 wait stopTime=`date +%s` echo "耗時(shí): $[$stopTime - $startTime] s" #關(guān)閉文件描述符的讀 exec 3<&- #關(guān)閉文件描述符的寫 exec 3>&-
其實(shí)主要就是mkfifo和exec兩個(gè)命令的使用。
exec命令
用于調(diào)用并執(zhí)行指令的命令。exec命令通常用在shell腳本程序中,可以調(diào)用其他的命令。如果在當(dāng)前終端中使用命令,則當(dāng)指定的命令執(zhí)行完畢后會(huì)立即退出終端。
Linux每打開一個(gè)shell就會(huì)打開默認(rèn)的三個(gè)文件描述符描0,1,2,分別代表標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出。需要的時(shí)候我們可以使用exec命令指定一個(gè)大于3的數(shù)字作為文件。
1、exec 3</tmp/1.txt #以“只讀方式”打開/tmp/1.txt,文件描述符對(duì)應(yīng)為3
2、exec 3>/tmp/1.txt #以“只寫方式”打開/tmp/1.txt,文件描述符對(duì)應(yīng)為3
3、exec 3<>/tmp/1.txt #以“讀寫方式”打開/tmp/1.txt,文件描述符對(duì)應(yīng)為3
4、exec 3<&- #關(guān)閉文件描述符3
轉(zhuǎn)載請(qǐng)注明:阿湯博客->Linux Shell腳本如何實(shí)現(xiàn)多線程 http://www.zhongjima.net/atang_4575.html