1. 程式人生 > >Shell指令碼中併發執行緒以及併發數的控制

Shell指令碼中併發執行緒以及併發數的控制

http://blog.chinaunix.net/uid-8478094-id-3995108.html

Shell指令碼中併發執行緒以及併發數的控制

主要記錄一下Shell指令碼中的命令的併發和序列執行。

預設的情況下,Shell指令碼中的命令是序列執行的,必須等到前一條命令執行完後才執行接下來的命令,但是如果我有一大批的的命令需要執行,而且互相又沒有影響的情況下(有影響的話就比較複雜了),那麼就要使用命令的併發執行了。

看下面的程式碼:
[plain] view plaincopy
#!/bin/bash

for(( i = 0; i < ${count}; i++ ))
do
commands1
done

commands2
對於上面的程式碼,因為每個commands1都挺耗時的,所以打算使用併發程式設計,這樣就可以節省大量時間了。

修改後的程式碼如下:

[plain] view plaincopy
#!/bin/bash

for(( i = 0; i < ${count}; i++ ))
do
{
commands1
}&
done

commands2

這樣的話commands1就可以並行執行了。 實質是將commands1作為後臺程序在執行,這樣主程序就不用等待前面的命令執行完畢之後才開始執行接下來的命令。

但是我的本來目的是讓commands1的這個迴圈都執行結束後,再用command2去處理前面的結果。如果像上面這樣寫的話,在commands1都還沒結束時就已經開始執行commands2了,得到了錯誤的結果。

再次修改程式碼如下:

[plain] view plaincopy
#!/bin/bash

for(( i = 0; i < ${count}; i++ ))
do
{
commands1
}&
done
wait

commands2

上面這樣就可以達到預期的目的了,先是所有的commands1在後臺並行執行,等到迴圈裡面的命令都結束之後才執行接下來的commands2。

對於上面的程式碼,如果count值特別大的時候,我們應該控制併發程序的個數,不然會影響系統其他程序的執行,甚至宕機。

下面的程式碼控制併發個數。其實是利用令牌原理實現,一個執行緒要執行,首先要拿到令牌在該程式碼中即read一行資料,讀取不到資料就會暫停,否則就拿到資料就執行命令任務,完成後將令牌再放回,也即再在管道檔案中寫入一行資料,這裡的資料是換行符,echo >&4。這樣另外的執行緒就可以再讀該資料(拿到令牌),並執行。

#!/bin/bash
tmpfile=$$.fifo #建立管道名稱
mkfifo KaTeX parse error: Expected 'EOF', got '#' at position 15: tmpfile #̲建立管道 exec 4<>tmpfile #建立檔案標示4,以讀寫方式操作管道$tmpfile
rm $tmpfile #將建立的管道檔案清除

thred=4 #指定併發個數
seq=(1 2 3 4 5 6 7 8 9 21 22 23 24 25 31 32 33 34 35) #建立任務列表

為併發執行緒建立相應個數的佔位

{
for (( i = 1;i<=${thred};i++ ))
do
echo; #因為read命令一次讀取一行,一個echo預設輸出一個換行符,所以為每個執行緒輸出一個佔位換行
done
} >&4 #將佔位資訊寫入管道

for id in ${seq[*]} #從任務列表 seq 中按次序獲取每一個任務 或者用:for id in ${seq}
do
read #讀取一行,即fd4中的一個佔位符
(./ur_command ${id};echo >&4 ) & #在後臺執行任務ur_command 並將任務 ${id} 賦給當前任務ur_command;任務執行完後在fd4種寫入一個佔位符 ,&表示該部分命令/任務 並行處理
done <&4 #指定fd4為整個for的標準輸入
wait #等待所有在此shell指令碼中啟動的後臺任務完成
exec 4>&- #關閉管道

整個流程中read 和 echo 對fd4的交替寫入和讀取是併發處理的關鍵
可以想象 如果read 命令發現fd4中沒有資料時 將等待fd4的資料

但是這有個缺點就是每個執行緒執行前都要拿到令牌,而且這個令牌的拿放是要消耗I/O的,有沒有更好的方法實現?
可以用PV語句實現。也就是定義一個全域性變數globleThread_num用來存放最大執行緒個數,還要有一個變數currentThread_num存放當前的執行緒個數,然後需要鎖,當讀取和改變currentThread_num的值的時候,需要鎖住。