情景linux--shell如何實現多線程?

来源:http://www.cnblogs.com/signjing/archive/2017/06/24/7074778.html
-Advertisement-
Play Games

情景linux shell如何實現多線程? 情景 shell腳本的執行效率雖高,但當任務量巨大時仍然需要較長的時間,尤其是需要執行一大批的命令時。因為預設情況下,shell腳本中的命令是串列執行的。如果這些命令相互之間是獨立的,則可以使用“併發”的方式執行這些命令,這樣可以更好地 利用系統資源,提升 ...


情景linux--shell如何實現多線程?

情景

shell腳本的執行效率雖高,但當任務量巨大時仍然需要較長的時間,尤其是需要執行一大批的命令時。因為預設情況下,shell腳本中的命令是串列執行的。如果這些命令相互之間是獨立的,則可以使用“併發”的方式執行這些命令,這樣可以更好地利用系統資源,提升運行效率,縮短腳本執行的時間。如果命令相互之間存在交互,則情況就複雜了,那麼不建議使用shell腳本來完成多線程的實現。

為了方便闡述,使用一段測試代碼。在這段代碼中,通過seq命令輸出1到10,使用for...in語句產生一個執行10次的迴圈。每一次迴圈都執行sleep 1,並echo出當前迴圈對應的數字。

註意:

  1. 真實的使用場景下,迴圈次數不一定等於10,或高或低,具體取決於實際的需求。
  2. 真實的使用場景下,迴圈體內執行的語句往往比較耗費系統資源,或比較耗時等。

請根據真實場景的各種情況理解本文想要表達的內容

$ cat test1.sh  
#/bin/bash

all_num=10

a=$(date +%H%M%S)

for num in `seq 1 ${all_num}`
do
    sleep 1
    echo ${num}
done

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"

通過上述代碼可知,為了體現執行的時間,將迴圈體開始前後的時間列印了出來。

運行結果:

$ sh test1.sh 
1
2
3
4
5
6
7
8
9
10
startTime:  193649
endTime:    193659

10次迴圈,每次sleep 1秒,所以總執行時間10s。

方案

方案1:使用"&"使命令後臺運行

在linux中,在命令的末尾加上&符號,則表示該命令將在後臺執行,這樣後面的命令不用等待前面的命令執行完就可以開始執行了。示例中的迴圈體內有多條命令,則可以以{}括起來,在大括弧後面添加&符號。

$ cat test2.sh 
#/bin/bash

all_num=10

a=$(date +%H%M%S)

for num in `seq 1 ${all_num}`
do
{
    sleep 1
    echo ${num}
} &
done

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"

運行結果:

sh test2.sh 
startTime:  194147
endTime:    194147
[j-tester@merger142 ~/bin/multiple_process]$ 1
2
3
4
5
6
7
8
9
10

通過結果可知,程式沒有先列印數字,而是直接輸出了開始和結束時間,然後顯示出了命令提示符[j-tester@merger142 ~/bin/multiple_process]$(出現命令提示符表示腳本已運行完畢),然後才是數字的輸出。這是因為迴圈體內的命令全部進入後臺,所以均在sleep了1秒以後輸出了數字。開始和結束時間相同,即迴圈體的執行時間不到1秒鐘,這是由於迴圈體在後臺執行,沒有占用腳本主進程的時間。

方案2:命令後臺運行+wait命令

解決上面的問題,只需要在上述迴圈體的done語句後面加上wait命令,該命令等待當前腳本進程下的子進程結束,再運行後面的語句。

$ cat test3.sh 
#/bin/bash

all_num=10

a=$(date +%H%M%S)

for num in `seq 1 ${all_num}`
do
{
    sleep 1
    echo ${num}
} &
done

wait

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"

運行結果:

$ sh test3.sh 
1
2
3
4
5
6
7
9
8
10
startTime:  194221
endTime:    194222

但這樣依然存在一個問題:
因為&使得所有迴圈體內的命令全部進入後臺運行,那麼倘若迴圈的次數很多,會使操作系統在瞬間創建出所有的子進程,這會非常消耗系統的資源。如果迴圈體內的命令又很消耗系統資源,則結果可想而知。

最好的方法是併發的進程是可配置的。

方案3:使用文件描述符控制併發數

$ cat test4.sh 
#/bin/bash

all_num=10
# 設置併發的進程數
thread_num=5

a=$(date +%H%M%S)


# mkfifo
tempfifo="my_temp_fifo"
mkfifo ${tempfifo}
# 使文件描述符為非阻塞式
exec 6<>${tempfifo}
rm -f ${tempfifo}

# 為文件描述符創建占位信息
for ((i=1;i<=${thread_num};i++))
do
{
    echo 
}
done >&6 


# 
for num in `seq 1 ${all_num}`
do
{
    read -u6
    {
        sleep 1
        echo ${num}
        echo "" >&6
    } & 
} 
done 

wait

# 關閉fd6管道
exec 6>&-

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"

運行結果:

$ sh test4.sh 
1
3
2
4
5
6
7
8
9
10
startTime:  195227
endTime:    195229

方案4:使用xargs -P控制併發數

xargs命令有一個-P參數,表示支持的最大進程數,預設為1。為0時表示儘可能地大,即方案2的效果。

$ cat test5.sh 
#/bin/bash

all_num=10
thread_num=5

a=$(date +%H%M%S)

seq 1 ${all_num} | xargs -n 1 -I {} -P ${thread_num} sh -c "sleep 1;echo {}"

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"

運行結果:

$ sh test5.sh 
1
2
3
4
5
6
8
7
9
10
startTime:  195257
endTime:    195259

方案5:使用GNU parallel命令控制併發數

GNU parallel命令是非常強大的並行計算命令,使用-j參數控制其併發數量。

$ cat test6.sh 
#/bin/bash

all_num=10
thread_num=6

a=$(date +%H%M%S)


parallel -j 5 "sleep 1;echo {}" ::: `seq 1 10`

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"

運行結果:

$ sh test6.sh 
1
2
3
4
5
6
7
8
9
10
startTime:  195616
endTime:    195618

總結

“多線程”的好處不言而喻,雖然shell中並沒有真正的多線程,但上述解決方案可以實現“多線程”的效果,重要的是,在實際編寫腳本時應有這樣的考慮和實現。
另外:
方案3、4、5雖然都可以控制併發數量,但方案3顯然寫起來太繁瑣。
方案4和5都以非常簡潔的形式完成了控制併發數的效果,但由於方案5的parallel命令非常強大,所以十分建議系統學習下。
方案3、4、5設置的併發數均為5,實際編寫時可以將該值作為一個參數傳入。

參考文章

  1. http://blog.csdn.net/qq_34409701/article/details/52488964
  2. https://www.codeword.xyz/2015/09/02/three-ways-to-script-processes-in-parallel/
  3. http://www.gnu.org/software/parallel/

相關知識點

  • wait命令
  • &後臺運行
  • 文件描述符、mkfifo等
  • xargs命令
  • parallel命令

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文轉自:[FFmpeg 入門(1):截取視頻幀 | www.samirchen.com][2] 背景 在 Mac OS 上如果要運行教程中的相關代碼需要先安裝 FFmpeg,建議使用 brew 來安裝: 或者你可以參考[在 Mac OS 上編譯 FFmpeg][5]使用源碼編譯和安裝 FFmpeg ...
  • 本文轉自:[AVAudioSession(4):響應音頻中斷事件 | www.samirchen.com][2] 本文內容主要來源於 [Responding to Interruptions][3]。 當一個音頻中斷到來時,會關閉你的 Audio Session,這通常就意味著停止或暫停你的音頻播放 ...
  • 本文轉自:[AVAudioSession(3):定製 Audio Session 的 Category | www.samirchen.com][2] 本文內容主要來源於 [Working with Categories][3]。 對於 Audio Session 來說,與之對應的 Category ...
  • 平時開發系統時偶爾會遇到數據超長導致往資料庫中保存時出錯。 使用下邊的腳本可以方便的找出超長的欄位。 1.通過正式表創建臨時表,修改臨時表中varchar、nvarchar的長度為max 2.數據手動寫入臨時表後,查找超長欄位 3.新建測試表 表截圖如下: 修改表名,運行 1.通過正式表創建臨時表, ...
  • 最近開發中遇到的一個MySQL主從延遲的坑,記錄並總結,避免再次犯同樣的錯誤。 ...
  • 大多數用戶在對於磁碟進行分區的時候都是習慣性的不給系統盤預留很大空間,其實這並不是一個好習慣。因為系統分區並不像我們想象的那樣會僅僅安裝一個操作系統,系統分區多數還是會承載操作系統主要應用軟體安裝任務。那麼當磁碟空間爆滿後,MySQL會發生什麼事呢?又應該怎麼應對? 會發生什麼事 當磁碟空間寫滿了之 ...
  • 連接資料庫的八大步驟 Step1:鏈接資料庫 mysqli_connect() 參數: ①主機地址 ②mysql用戶名 ③mysql密碼 ④選擇連接的資料庫 ⑤埠號 返回:如果連接成功,返回資源類型的標誌符號;如果連接失敗,返回false。 如果我們與mysql建立的連接不只一條,那麼以後操作數據 ...
  • redis對象 redis中有五種常用對象 我們所說的對象的類型大多是值的類型,鍵的類型大多是字元串對象,值得類型大概有以下幾種,但是無論哪種都是基於redisObject實現的 redisObject的結構如下 type的可選值有五種.分別是 REDIS_STRING, REDIS_LIST, R ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...