Shell 腳本天生單線程,但通過後台任務(&)、命名管道(FIFO)與文件描述符技巧,可實現高效的並發控制與線程池模型。本文由淺入深,演示從無序並發到固定併發數的完整進化路徑,並給出生產級模板。
command &:將命令放入後台執行,Shell 立即繼續執行下一條指令。wait:阻塞當前 Shell,直到所有後台子進程結束。{ ... } &:將代碼塊作為整體放入後台,實現邏輯分組並發。pairs=5
port=9080
port_m=27017
for ((i=1; i<=pairs; i++)); do
mongo="mongo_00$i"
acmeair_web="acmeair_web_00$i"
{
docker run --name "$mongo" -d -p "$port_m":27017 mymongodb
docker run -d -p "$port":9080 --name "$acmeair_web" --link "${mongo}:mongo" acmeair/web
} &
((port++))
((port_m++))
done
wait
echo "All $pairs pairs started."
缺點:併發數完全由外層循環決定(此處 5),無法動態控制,大量任務時會耗盡系統資源(PID、端口、內存)。
FIFO 文件 <---> 文件描述符 6
初始:向 FD6 寫入 N 個換行符(令牌)
任務啟動:read -u6 消耗 1 個令牌 → 執行任務
任務結束:echo >&6 歸還 1 個令牌
無令牌時 read 阻塞 → 自然限制併發數
#!/bin/bash
set -euo pipefail
MAX_THREADS=15
TOTAL_TASKS=50
FIFO_FILE="/tmp/$$.fifo"
# 1. 創建 FIFO 並綁定 FD6
mkfifo "$FIFO_FILE"
exec 6<>"$FIFO_FILE"
rm -f "$FIFO_FILE" # 刪除文件實體,FD6 仍持有引用
# 2. 預填充令牌
for ((i=0; i&6
done
# 3. 任務分發循環
for ((i=0; i&6 # 歸還令牌
} &
done
# 4. 等待所有後台任務完成
wait
# 5. 清理
exec 6>&-
echo "All $TOTAL_TASKS tasks completed with max $MAX_THREADS concurrent."
| 代碼段 | 作用 |
|---|---|
exec 6<> "$FIFO_FILE" | 雙向打開 FIFO 綁定 FD6,避免 read 時 EOF |
rm -f "$FIFO_FILE" | 刪除文件系統條目,防止殘留;FD6 仍有效 |
for ...; do echo >&6; done | 初始化令牌池,數量 = 最大併發數 |
read -u6 | 原子性消耗 1 個令牌(換行符),無則阻塞 |
{ ... } & | 任務邏輯放入後台,主循環繼續分發 |
echo >&6 | 任務結束歸還令牌,喚醒下一個阻塞的 read |
wait | 回收所有子進程,防止殭屍進程 |
{
# 任務主體
docker run ... && {
echo "subprocess finished"
} || {
echo "ERROR: task $task_id failed" >&2
# 可選:記錄失敗任務 ID 供重試
echo "$task_id" >> /tmp/failed_tasks.txt
}
echo >&6
} &
set -euo pipefail 開啟嚴格模式。$? 或用 && / || 鏈式處理。# 將任務列表寫入文件或管道,xargs -P 控制併發
seq 1 50 | xargs -P 15 -I {} bash -c '
task_id={}
echo "Task $task_id start"
sleep $((RANDOM % 3 + 1))
echo "Task $task_id done"
'
優點:一行命令搞定,無需管理 FIFO。
局限:複雜邏輯難寫、錯誤處理弱、無法動態調整併發數。
MAX_THREADS=$(nproc) 或 $(nproc --all)。command & 無限制,必須用令牌池或 xargs -P。trap 捕獲信號)務必 exec 6>&- 關閉 FD,wait 回收子進程。
| 模式 | 適用場景 | 併發控制 | 複雜度 |
|---|---|---|---|
| 後台 & wait | 少量固定任務、無需限流 | 無(全併發) | 低 |
| FIFO 令牌池 | 大量任務、需精確限制併發、複雜邏輯 | 精確(固定 N) | 中 |
| xargs -P | 簡單命令批量執行、一次性腳本 | 固定 N | 極低 |
| GNU Parallel | 生產級批量處理、多機分佈、進度條、重試 | 動態、負載感知 | 低(外部工具) |
建議:日常運維首選 xargs -P;核心業務腳本採用 FIFO 模板;大規模數據處理引入 GNU Parallel。掌握多線程模型,讓 Shell 腳本突破單線程瓶頸,承擔更重的自動化使命。
Linux ACL 存取控制列表實戰:突破傳統 UGO 權限、精細授權多用戶協作
Linux 安裝 Node.js 三大方式詳解:預編譯、原始碼、套件管理器
Linux 取得指令幫助的六大管道:內建、man、info、文件、原始碼、搜尋引擎
Linux 用 psensor 監控 CPU/顯卡/硬碟溫度:lm-sensors + hddtemp + 圖形化前端完整部署
Ubuntu 清理舊核心釋放 /boot 空間:安全移除不再使用的 Linux Kernel
Linux 安裝 APC PowerChute Network Shutdown (PCNS) 完整部署指南
Linux 新增靜態路由指令詳解:臨時/永久、預設/主機/網路路由完整範例
CentOS wget 進度條多行顯示亂碼修復:locale 翻譯錯誤導致換行、msgunfmt/msgfmt 修正 .mo 檔案