#!/bin/bash trap "exec 1000>&-;exec 1000<&-;exit 0" 2 # 分別爲 建立管道文件,文件操做符綁定,刪除管道文件 mkfifo testfifo exec 1000<>testfifo rm -rf testfifo # 對文件操做符進行寫入操做。 # 經過一個for循環寫入10個空行,這個10就是咱們要定義的後臺線程數量。 for ((n=1; n<=10; n++)) do echo >&1000 done # 建立一個備份目錄 if [[ ! -d back ]]; then mkdir back fi # 開始時間記錄 start=`date "+%s"` # 獲取URL總數,若是總數爲0,直接退出 total=`cat urls | wc -l` if [[ $total == 0 ]]; then echo 'urls總數爲空' exit 0 fi # 遍歷URLS文件,開始執行下載 for ((i=1;i<=$total;i++)) do { # 從testfifo中讀取一行 read -u1000 { # 增長嘗試次數,最大5次 for j in {1..5} do # 判斷單獨進程文件目錄是否存在,不存在則建立目錄 download_dir=audio"$i" if [[ ! -d $download_dir ]]; then mkdir -p $download_dir fi echo "往目錄${download_dir}中開始下載文件,嘗試次數:${j}" # 讀取URLS中的一行,下載文件 you-get -o $download_dir `sed -n "$i"p urls | tr -d '\r'` # 校驗是否有異常,若是沒有異常,則跳出循環,執行外下一條,若是有異常,再次嘗試下載 if [[ $? != 0 ]]; then mv $download_dir/* back rm -rf $download_dir else break fi done # 向文件操做符中寫入一個空行 echo >&1000 }& } done # 等待全部任務完成 wait end=`date "+%s"` echo "time: `expr $end - $start`" exec 1000>&- exec 1000<&-
所謂多進程,就是將一個任務劃分紅多個子任務放在後臺執行。"FIFO"是一種特殊的文件類型,它容許獨立的進程通信. 一個進程打開FIFO文件進行寫操做,而另外一個進程對之進行讀操做, 而後數據即可以如同在shell或者其它地方常見的的匿名管道同樣流線執行。默認狀況下,建立的FIFO的模式爲0666('a+rw')減去umask中設置的位。html
爲了比較並行和串行的區別,咱們先寫個串行的腳本:linux
#!/bin/bash start=`date "+%s"` for i in {1..10} do echo $i; sleep 2 done end=`date "+%s"` echo "Time: `expr $end - $start`"
執行結果以下:shell
$ sh series.sh 1 2 3 4 5 6 7 8 9 10 Time: 21
從結果來開,執行完上面10次任務,每次任務2秒,總耗時21秒,符合代碼的邏輯。bash
先將上面的串行任務改爲多線程並行任務。微信
#!/bin/bash start=`date "+%s"` for i in {1..10} do { echo $i; sleep 2 }& done wait end=`date "+%s"` echo "Time: `expr $end - $start`"
執行上面腳本的結果以下:多線程
$ sh paralle.sh 1 2 3 4 5 6 7 8 9 10 Time: 2
一般,用{}
將不佔處理器卻很耗時的任務放包裝一個塊,經過&
放置在後臺運行,以達到節約時間的效果。上面並行代碼,咱們把10次任務所有放在後臺執行,每一個人物耗時2秒,因爲並行執行,總耗時也就是Max(任務耗時)=2秒。測試
{ echo $i; sleep 2 }&
在小任務跟前,這種方式運用起來駕輕就熟,可是在任務量過大的時候,這種方式的缺點也就顯而易見了:沒法控制運行在後臺的進程數,不能就10萬個任務就是跑10萬個進程吧。爲了控制進程,咱們引入了管道
和文件操做符
。網站
管道就像水管,有流入纔會有流出,水管數水流的通道,管道是數據的通道。管道分爲無名管道和有名管道。url
管道類別 | 命令 | 栗子 | ||
---|---|---|---|---|
無名管道 | 經常使用的` | `就是管道,只不過是無名的,能夠直接做爲兩個進程的數據通道 | `echo "hello world, I'm a test" | grep "test"` |
有名管道 | mkfilo 能夠建立一個管道文件 |
mkfiflo testfifo |
管道有一個特色,若是管道中沒有數據,那麼取管道數據的操做就會阻塞,直到管道內進入數據,而後讀出後纔會終止這一操做,同理,寫入管道的操做若是沒有讀取操做,這一個動做也會阻塞。spa
當經過echo命令往fifotest管道中寫入數據時,因爲沒有任何其餘消費進程對管道操做,因此,該管道阻塞,直到再打開一個窗口且經過cat操做該管道。
同理,先操做讀取管道也會出現阻塞的狀況。
經過以上實驗,看以看到,僅僅一個管道文件彷佛很難實現控制後臺線程數,所以咱們接下來簡單介紹 文件操做符
。
系統運行起始,就相應設備自動綁定到了 三個文件操做符 分別爲0
、1
、2
對應 stdin
、stdout
、 stderr
。在 /proc/self/fd
或者/dev/fd
中能夠看到這三個對應文件:
輸出到這三個文件的內容都會顯示出來。只是由於顯示器做爲最經常使用的輸出設備而被綁定。
在Linux中,能夠經過exec
指令自行定義、綁定文件操做符,文件操做符通常從3~(n-1)均可以隨便使用,此處的n爲ulimit -n
的定義值。
從上圖能夠看出本機的n
值爲8192
,因此文件操做符只能使用0-8192
,可自行定義的就只能是3-8192
。
雖然exec和source都是在父進程中直接執行,但exec這個與source有很大的區別,source是執行shell腳本,並且執行後會返回之前的shell。而exec的執行不會返回之前的shell了,而是直接把之前登錄shell做爲一個程序看待,在其上經行復制。exec可參考此文:《linux 下的 mkfifo、exec 命令使用》
第3行:
exec 1000<>testfifo
來實現,但關閉時必須分開來寫。>
讀的綁定,<
標識寫的綁定 <>
則標識對文件描述符1000的全部操做,其等同於對管道文件testfifo的操做。第6-8行:
建立管道文件
,文件操做符綁定
,刪除管道文件
第12-15行:
行
爲單位的。第32-61行:
$total
個任務($total是變量,是讀取的urls的總行數,值大於0),咱們須要保證後臺只有10個進程在同步運行(固然這段代碼有點小遺憾,就是未能根據總行數決定用多少個進程,加入總行數小於10,但咱們建立了10行空字符串,但這並不影響咱們的測試) 。read -u1000
的做用是:讀取一次管道中的一行,在這兒就是讀取一個空行。read -u1000
這兒始終停頓。you-get
下載url中的圖片、語音,若是下載失敗,最多嘗試5次。關於you-get
參考這篇文章《You-Get:支持 80 多個網站的命令行多媒體下載器》瞭解其更多。第64-69行:
exec 1000>&-
和exec 1000<&-
是關閉 fd1000
。該文首發《虛懷若谷》我的博客,轉載前請務必署名,轉載請標明出處。
古之善爲道者,微妙玄通,深不可識。夫惟不可識,故強爲之容:豫兮若冬涉川,猶兮若畏四鄰,儼兮其若客,渙兮若冰之釋,敦兮其若樸,曠兮其若谷,混兮其若濁。
孰能濁以靜之徐清?孰能安以動之徐生?
保此道不欲盈。夫惟不盈,故能敝而新成。
請關注個人微信公衆號:下雨就像彈鋼琴,Thanks♪(・ω・)ノ