1. for循環並發執行 - 前台命令變后台進程
1.1 用法
for((i=0;i<$num_tasks;i++)) { }& done wait echo "done"
1.2 問題
以上的實現方法中,同時有num_tasks在后台運行,如果num_tasks個數非常大,那么很可能爆內存。那么如果協調內存和並發效率呢?
1.3 解決方法
控制同時啟動的進程的個數。
2. 控制並發執行的進程個數 - 管道 + 文件操作符實現隊列
2.1 Prerequisities
2.1.1 管道文件
1:無名管道(ps aux | grep nginx)
2:有名管道(mkfifo /tmp/fd1)
有名管道特性:
1. mkfifo /tmp/fd1 創建有名管道
cat /tmp/fd1 顯示管道中內容,如果管道內容為空,則阻塞
2. echo "test" > /tmp/fd1 如果沒有讀管道的操作,則阻塞
在terminal 1 中執行以下命令,向管道中輸入'test',由於沒有讀管道的操作,所以被阻塞了
在terminal 2 中執行以下命令,讀管道中的內容,於是terminal 1 結束了阻塞的狀態。
terminal 2 有了輸出test,而terminal 1中命令終止。
可以利用以上特性維護一個存令牌的隊列,向其中寫入一個令牌,被阻塞,只能這個令牌被讀取之后,才可以結束阻塞的狀態;
如果用於並發進程的控制,以上特性可以用於保證並發的進程數是1。
===== 但是,如果想每次並發多個進程要如何處理呢? =====
可以看出管道特性中,阻礙“並發多個”的點在於 “寫入一個即阻塞”,即限制了令牌隊列的長度為1;
那么如果可以不被阻塞,而可以由我們制定令牌長度,這樣並發數就是我們指定的隊列長度,也即隊列中的初始寫入的令牌數了。
如果希望實現“不被阻塞”,那么可以采用“文件描述符”~
2.1.2 文件描述符
1. 簡介
文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。(百度百科)
2. 文件描述符與管道文件關聯
exec [文件描述符fid] <> [管道文件fd]
該命令含義:創建文件描述符fid(非負整數,可以避開使用0 stdin, 1 stdout, 2 stderr),並關聯(以讀寫方式 <> 打開)管道文件fd。
此時文件描述符fid就擁有了管道的所有特性,還具有一個管道不具有的特性:無限存不阻塞,無限取不阻塞,而不用關心管道內是否為空,也不用關心是否有內容寫入引用文件描述符&fid。
可以執行n次echo >&fid 往管道里放入n個令牌。
2.2 shell 指定並發數的實現
2.2.1 實現邏輯
設置這個隊列的長度為可以並發執行的進程個數$num_para。
(1) 先往這個管道中放置num_para個令牌;
(2) 來了num_para個進程,取走了num_para個令牌;第num_para + 1個進程因為取不到令牌,被阻塞;
(3) 最初取到令牌的進程中,有一個執行完了,釋放令牌到管道中;
(4) 管道中又有令牌了,一個被阻塞的進程可以獲取該令牌,執行;
(5) 循環(3)(4)直至所有進程執行完畢。
2.2.2 實現代碼
start_time=`date +%s` # 定義腳本運行的開始時間 [ -e /tmp/fd1 ] || mkfifo /tmp/fd1 # 如果有名管道文件不存在,則創建 exec 3<>/tmp/fd1 # 創建文件描述符3, 並以可讀<, 可寫>的方式關聯管道文件 # 這時文件描述符3就有了有名管道文件的特性 rm -rf /tmp/fd1 # 關聯后的文件描述符擁有管道文件的所有特性,所以這時候管道文件可以刪除 # 留下文件描述符來用就可以了 for((i=1;i<=10;i++)) # &3 引用文件描述符3,循環向管道中放入了10個令牌,支持並發數為10 do echo >&3 done for((i=1;i<=100;i++)) do read -u3 # 從管道中讀取1個令牌 { sleep 1 # 模擬進程運行 echo 'success'$i echo >&3 # 該進程運行結束,將其令牌放回管道 }& done wait stop_time=`date +%s` # 定義腳本運行的結束時間 echo "Time: `expr $stop_time - $start_time`" # 獲取執行100條進程總的運行時間 exec 3<&- # 關閉文件描述符的讀 exec 3>&- # 關閉文件描述符的寫
輸出:
success1...省略
參考鏈接:
1. shell隊列實現線程並發控制:https://www.cnblogs.com/chenjiahe/p/6268853.html
2. shell實現多線程筆記:https://blog.51cto.com/mochaming/1279864
3. exec操作文件描述符:http://blog.sina.com.cn/s/blog_7099ca0b0100nby8.html
4. 文件描述符:https://blog.csdn.net/Captain_MXD/article/details/52153233