Bash 原生不支援執行緒,但透過後台作業(&)與命名管道即可實現並發控制。本文從最簡單的「全量並發」進階到「固定線程池」,並提供 Docker 容器啟動、錯誤處理、資源清理的生產級範本,助你在自動化運維、批次處理場景大幅縮短執行時間。

利用 { ... } & 將任務丟入背景,wait 等待所有子進程結束。
#!/bin/bash
pairs=5
port=9080
port_m=27017
for ((i=1; i<=pairs; i++)); do
mongo="mongo_00$i"
web="acmeair_web_00$i"
{
docker run -d --name "$mongo" -p "$port_m:27017" mymongodb
docker run -d --name "$web" -p "$port:9080" --link "$mongo:mongo" acmeair/web
} &
((port++))
((port_m++))
done
wait # 等待所有背景作業完成
echo "All containers started."
缺點: 並發數不可控,大量任務會耗盡 CPU/記憶體/檔案描述符,甚至觸發 OOM Killer。
N 個換行符(Token),代表可用線程槽位。read -u6 取走一個 Token(槽位滿則阻塞)。echo >&6 歸還 Token。#!/bin/bash
set -euo pipefail
THREADS=15 # 最大並發數
TOTAL_TASKS=50 # 總任務數
FIFO_FILE="/tmp/$$.fifo"
# ---- 初始化信號量 ----
mkfifo "$FIFO_FILE"
exec 6<>"$FIFO_FILE"
rm -f "$FIFO_FILE" # 刪除檔案實體,FD 仍有效
# 填充 Token
for ((i=0; i&6
done
# ---- 任務函數 ----
run_task() {
local task_id=$1
echo "[$(date '+%H:%M:%S')] Task $task_id START"
# 實際業務邏輯(此處以 sleep 模擬)
if docker run --rm alpine sleep 2; then
echo "[$(date '+%H:%M:%S')] Task $task_id OK"
else
echo "[$(date '+%H:%M:%S')] Task $task_id FAILED" >&2
return 1
fi
}
# ---- 主迴圈 ----
for ((i=1; i<=TOTAL_TASKS; i++)); do
read -u6 # 取 Token(阻塞直到有空槽)
{
run_task "$i"
echo >&6 # 歸還 Token
} &
done
wait # 等待所有背景作業
exec 6>&- # 關閉 FD
echo "All tasks completed."
| 語法 | 作用 |
|---|---|
exec 6<>file | 雙向開啟 FD6,避免「讀端關閉導致寫端收到 SIGPIPE」 |
read -u6 | 從 FD6 讀取一行(消耗一個 Token) |
echo >&6 | 向 FD6 寫入一行(歸還 Token) |
{ ... } & | 將任務包裹為背景子 Shell,不阻塞主迴圈 |
set -euo pipefail | 嚴格模式:變數未設定/指令失敗/管道失敗即退出 |
GNU xargs -P 內建並行,適合「命令列參數化」的任務:
# 並行 15 執行 docker 啟動
seq 1 50 | xargs -P 15 -I {} bash -c '
id={}
docker run --rm --name "task_$id" alpine sleep 2
'
優勢: 程式碼極簡、自動負載均衡。劣勢: 複雜邏輯(錯誤處理、狀態收集)較難實現。
# 安裝:apt/yum/pacman install parallel
seq 1 50 | parallel -j 15 --eta --joblog /tmp/parallel.log \
'docker run --rm --name task_{} alpine sleep 2'
--eta:顯示預估剩餘時間。--joblog:記錄每任務狀態、耗時、退出碼。--retries 2:失敗自動重試。ulimit -u 限制用戶進程數,防止 Fork 炸彈。wait $pid 檢查 $?)。trap 'rm -f "$FIFO_FILE"; exec 6>&-; kill 0' EXIT INT TERM 確保異常退出時釋放資源。> /tmp/task_$i.log 2>&1),避免標準輸出交錯。nproc、記憶體、檔案描述符限制設定 THREADS,通常 2×CPU 核心數為宜。從 & + wait 到 FIFO 線程池,再到 xargs -P/GNU Parallel,Bash 並發技法各有甜點。簡單批次用 xargs,複雜流程控制用 FIFO 池,生產環境首選 GNU Parallel。掌握這些模式,讓你的自動化腳本在多核伺服器上跑出單核數十倍的吞吐量。