Linux Shell腳本如何實現(xiàn)多線程

2020年10月13日16:56:32 發(fā)表評論 4,887 ℃

當我們工作中遇到一些批量分發(fā)、批量執(zhí)行場景時,寫的一些shell腳本都是單線程任務,當然這些量級不大的時候,看不出劣勢。

舉個例子:現(xiàn)在需要通過跳板機,分發(fā)一個文件到10臺服務器,每臺服務器傳輸需要1s時間,10臺服務器傳輸完就是10s。看下面腳本,這里通過sleep命令模擬傳輸耗時。

#!/bin/bash
#Author: m.maowutv.com
#Description: 模擬分發(fā)文件到10臺服務器
startTime=`date +%s`
for i in `seq 10`;do
    echo $i
    sleep 1
done
stopTime=`date +%s`
echo "耗時: $[$stopTime - $startTime] s"

這個執(zhí)行結果不用思考,就是10s。

# bash test.sh 
1
2
3
4
5
6
7
8
9
10
耗時: 10 s

如果是500臺服務器或者1000臺服務器,那命令執(zhí)行以后就慢慢等吧。那如果文件比較大,一個需要30s呢?1000 * 30 =30000 s 基本上就是8個多小時。早上上班執(zhí)行,等到下班剛剛好,如果你的腳本是前臺運行,中間突然網絡波動,導致遠程斷開了,那就廁所哭去吧。

其實文件分發(fā)大不了就是等嘛,但是如果是批量檢查服務器狀態(tài),或者批量啟動服務,服務器數(shù)量一大,那劣勢就特別明顯了。

那么有沒有辦法提高效率呢?答案是肯定的,利用多線程,本來需要10s完成的任務,我使用了5個線程,那么跑下來基本也就2s左右。shell不像 python、go語言,他們本身就有多線程模塊。

shell只能利用管道和文件描述符實現(xiàn)多線程任務,直接腳本測試剛才的分發(fā)文件。

#!/bin/bash
#Author: m.maowutv.com
#Description: 模擬多線程分發(fā)文件到10臺服務器
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 "耗時: $[$stopTime - $startTime] s"
exec 3<&-
exec 3>&-

執(zhí)行結果如下圖:

Linux Shell腳本如何實現(xiàn)多線程

如果是10個線程,結果就是1s,如果是6、7、8、9個線程,整個任務執(zhí)行完也是2s,所以需要根據(jù)實際的情況設置線程數(shù),也并不是線程越多越好,整個要看本身執(zhí)行腳本的服務器CPU核數(shù)以及其它資源情況。

下面對腳本語句進行分析:

#!/bin/bash
#Author: m.maowutv.com
#Description: 模擬多線程分發(fā)文件到10臺服務器
startTime=`date +%s`
tmpfifo="/tmp/$$.fifo"

#判斷有名管道是否存在 創(chuàng)建有名管道文件
[ -e $tmpfifo ] || mkfifo $tmpfifo 

#創(chuàng)建文件描述符 exec 3<>$tmpfifo,(可讀(<)可寫(>))創(chuàng)建文件描述符3關聯(lián)管道文件,這時候3這個文件描述符就擁有了管道的所有特性。
#還具有一個管道不具有的特性:無限存不阻塞,無限取不阻塞,而不用關心管道內是否為空,也不用關心是否有內容寫入引用文件描述符: &3可以執(zhí)行n次echo >&3 往管道里放入n把鑰匙。并發(fā)n
exec 3<>$tmpfifo

#關聯(lián)后的文件描述符擁有管道文件的所有特性,所以這時候管道文件可以刪除,留下文件描述符來用就可以了。
rm -rf $tmpfifo

for i in `seq 5`;do
    #&3代表引用文件描述符3,這條命令代表往管道里面放入了一個"令牌",`seq 5` 循環(huán)5次,也就是5個令牌,即5個線程
    echo >&3
done

for i in `seq 10`;do
    #代表從管道中讀取一個令牌
    read -u3
    {
        sleep 1
        echo $i
        #代表我這一次命令執(zhí)行到最后,把令牌放回管道
        echo >&3
    }&   #用{}把循環(huán)體括起來,后加一個&符號,代表每次循環(huán)都把命令放入后臺運行
done

#wait命令的意思是,等待(wait命令)上面的命令(放入后臺的)都執(zhí)行完畢了再往下執(zhí)行
wait

stopTime=`date +%s`
echo "耗時: $[$stopTime - $startTime] s"

#關閉文件描述符的讀
exec 3<&-
#關閉文件描述符的寫
exec 3>&-

其實主要就是mkfifo和exec兩個命令的使用。

exec命令 

用于調用并執(zhí)行指令的命令。exec命令通常用在shell腳本程序中,可以調用其他的命令。如果在當前終端中使用命令,則當指定的命令執(zhí)行完畢后會立即退出終端。

Linux每打開一個shell就會打開默認的三個文件描述符描0,1,2,分別代表標準輸入,標準輸出和標準錯誤輸出。需要的時候我們可以使用exec命令指定一個大于3的數(shù)字作為文件。

1、exec 3</tmp/1.txt     #以“只讀方式”打開/tmp/1.txt,文件描述符對應為3

2、exec 3>/tmp/1.txt     #以“只寫方式”打開/tmp/1.txt,文件描述符對應為3

3、exec 3<>/tmp/1.txt    #以“讀寫方式”打開/tmp/1.txt,文件描述符對應為3

4、exec 3<&-             #關閉文件描述符3

轉載請注明:阿湯博客->Linux Shell腳本如何實現(xiàn)多線程 http://m.maowutv.com/atang_4575.html

【騰訊云】云服務器、云數(shù)據(jù)庫、COS、CDN、短信等云產品特惠熱賣中

發(fā)表評論

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: