Shell脚本如何实现并发执行控制

wen 实用脚本 1

本文目录导读:

Shell脚本如何实现并发执行控制

  1. 后台运行 + wait(最简单)
  2. 信号量控制(限制并发数)
  3. xargs 实现并发控制(最推荐)
  4. GNU parallel(功能最强大)
  5. 完整的生产环境示例
  6. 选择建议

在Shell脚本中实现并发执行控制,主要有以下几种常用方法,这里按照从简单到复杂的顺序介绍。

后台运行 + wait(最简单)

这是最基础的并发方式,使用 & 将任务放到后台,然后用 wait 等待所有完成。

#!/bin/bash
# 定义任务函数
task() {
    local id=$1
    echo "任务 $id 开始"
    sleep $((RANDOM % 5 + 1))
    echo "任务 $id 结束"
}
# 并发执行多个任务
for i in {1..5}; do
    task $i &
done
# 等待所有后台任务完成
wait
echo "所有任务已完成"

优缺点

  • 优点:简单直接
  • 缺点:无并发数量限制,可能瞬间启动过多进程导致系统负载过高

信号量控制(限制并发数)

使用 mkfifo 创建命名管道实现信号量,控制同时运行的进程数量。

#!/bin/bash
# 并发控制函数 - 使用命名管道作为信号量
concurrent_control() {
    local max_concurrent=$1  # 最大并发数
    local task_file=$2       # 任务列表文件
    # 创建临时FIFO(命名管道)作为信号量
    local fifo_file=$(mktemp -u)
    mkfifo $fifo_file
    exec 3<>$fifo_file
    rm -f $fifo_file
    # 初始化信号量:向管道写入max_concurrent个令牌
    for ((i=0; i<$max_concurrent; i++)); do
        echo >&3
    done
    # 读取任务并执行
    while IFS= read -r task; do
        # 从管道读取令牌(无令牌时会阻塞)
        read -u 3
        # 启动任务
        (
            echo "执行任务: $task"
            eval "$task"  # 实际执行任务
            echo "任务完成: $task"
            # 任务结束后归还令牌
            echo >&3
        ) &
    done < "$task_file"
    # 等待所有任务完成
    wait
    # 清理
    exec 3>&-
}
# 使用示例
cat > /tmp/tasks.txt << EOF
sleep 2; echo "任务1"
sleep 3; echo "任务2"
sleep 1; echo "任务3"
sleep 4; echo "任务4"
sleep 2; echo "任务5"
sleep 3; echo "任务6"
EOF
# 限制最多同时运行3个任务
concurrent_control 3 /tmp/tasks.txt

xargs 实现并发控制(最推荐)

xargs-P 参数可以非常方便地控制并发数。

#!/bin/bash
# 定义要执行的任务
task() {
    local id=$1
    echo "任务 $id 开始 (PID: $$)"
    sleep $((RANDOM % 5 + 2))
    echo "任务 $id 结束"
}
# 导出函数,使xargs可以访问
export -f task
# 生成任务参数列表
seq 1 10 | xargs -I {} -P 5 bash -c 'task "$@"' _ {}
echo "所有任务完成"

更实用的示例

#!/bin/bash
# 批量下载文件,最多3个并发
download_file() {
    local url=$1
    local filename=$(basename $url)
    echo "开始下载: $filename"
    wget -q $url -O /tmp/$filename
    echo "下载完成: $filename"
}
export -f download_file
# 从文件读取URL列表,并发3个下载
cat urls.txt | xargs -I {} -P 3 bash -c 'download_file "$@"' _ {}

GNU parallel(功能最强大)

如果系统安装了 parallel 命令,这是处理并发任务的最佳选择。

#!/bin/bash
# 安装GNU parallel(如果未安装)
# Ubuntu/Debian: apt-get install parallel
# CentOS/RHEL: yum install parallel
# 基本使用
task() {
    local id=$1
    echo "任务 $id"
    sleep 2
    echo "任务 $id 完成"
}
export -f task
# 并发5个任务
parallel -j 5 task ::: {1..20}
# 从文件读取参数
parallel -j 3 -a params.txt task
# 在处理命令时保留输出顺序
parallel --keep-order -j 4 task ::: {1..12}

GNU parallel的高级特性

#!/bin/bash
# 分布式执行
parallel --sshlogin server1,server2 -j 2 task ::: {1..10}
# 错误处理
parallel --halt now,fail=1 -j 3 task ::: {1..10}
# 进度显示
parallel --progress -j 4 task ::: {1..100}
# 结果保存
parallel --results output/ -j 4 task ::: {1..10}

完整的生产环境示例

结合了多种技术的实际应用:

#!/bin/bash
# 生产环境的并发任务管理器
set -euo pipefail
# 配置
MAX_CONCURRENT=4
LOG_DIR="/var/log/concurrent_tasks"
PID_FILE="/tmp/concurrent_tasks.pid"
# 日志函数
log() {
    local level=$1
    local msg=$2
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $msg"
}
# 任务函数
process_item() {
    local item=$1
    local task_id=$2
    log "INFO" "【任务$task_id】开始处理: $item"
    # 模拟处理时间
    sleep $((RANDOM % 5 + 2))
    # 模拟失败情况
    if [ $((RANDOM % 10)) -eq 0 ]; then
        log "ERROR" "【任务$task_id】处理失败: $item"
        return 1
    fi
    log "INFO" "【任务$task_id】处理成功: $item"
}
# 主函数
main() {
    local task_list=("数据A" "数据B" "数据C" "数据D" "数据E" "数据F" "数据G")
    local total=${#task_list[@]}
    local success_count=0
    local fail_count=0
    # 创建日志目录
    mkdir -p "$LOG_DIR"
    log "INFO" "开始并发处理 $total 个任务,最大并发数: $MAX_CONCURRENT"
    # 导出函数和变量
    export -f process_item log
    export LOG_DIR
    # 使用xargs控制并发
    for i in "${!task_list[@]}"; do
        echo "${task_list[$i]} $i"
    done | xargs -n 2 -P $MAX_CONCURRENT bash -c 'process_item "$1" "$2"' _
    log "INFO" "所有任务处理完成"
}
main "$@"

选择建议

  1. 简单场景(并发数少,不敏感):使用 & + wait
  2. 需要控制并发数:使用 xargs -P 或命名管道
  3. 复杂场景(错误处理、分布式):使用 GNU parallel
  4. 脚本可移植性重要:使用命名管道方式

建议:对于大多数生产环境,xargs -P 是最平衡的选择——既简单又功能完整。

抱歉,评论功能暂时关闭!