Shell脚本如何实现条件变量同步

wen 实用脚本 5

本文目录导读:

Shell脚本如何实现条件变量同步

  1. 使用waitnotify(通过文件信号)
  2. 使用FIFO命名管道
  3. 使用flock文件锁
  4. 使用trap和信号
  5. 复合条件变量(使用coproc进程替代)
  6. 最佳实践建议

在Shell脚本中,条件变量同步通常通过以下机制实现:

使用waitnotify(通过文件信号)

简单文件锁方式

#!/bin/bash
# 创建信号文件
SIGNAL_FILE="/tmp/signal_$$"
# 清理函数
cleanup() {
    rm -f "$SIGNAL_FILE"
}
trap cleanup EXIT
# 生产者函数
producer() {
    echo "生产者: 准备数据..."
    sleep 2  # 模拟耗时操作
    echo "ready" > "$SIGNAL_FILE"
    echo "生产者: 数据就绪"
}
# 消费者函数
consumer() {
    echo "消费者: 等待数据..."
    while [ ! -f "$SIGNAL_FILE" ]; do
        sleep 0.1  # 忙等待,但用sleep降低CPU占用
    done
    echo "消费者: 接收到数据: $(cat $SIGNAL_FILE)"
}
# 启动生产者和消费者
producer &
consumer &
wait

使用FIFO命名管道

#!/bin/bash
FIFO_FILE="/tmp/condition_fifo_$$"
# 创建FIFO
mkfifo "$FIFO_FILE"
trap "rm -f $FIFO_FILE" EXIT
# 生产者
producer() {
    echo "准备数据..."
    sleep 2
    echo "ready" > "$FIFO_FILE"  # 写入会阻塞直到消费者读取
    echo "数据已发送"
}
# 消费者
consumer() {
    echo "等待数据..."
    read data < "$FIFO_FILE"  # 阻塞等待
    echo "接收到数据: $data"
}
producer &
consumer &
wait

使用flock文件锁

#!/bin/bash
LOCK_FILE="/tmp/condition_lock_$$"
trap "rm -f $LOCK_FILE" EXIT
# 条件满足时写入
set_condition() {
    (
        flock -x 200  # 获取排他锁
        echo "ready" > "$LOCK_FILE"
        echo "条件已设置"
    ) 200>"$LOCK_FILE.lock"
}
# 等待条件
wait_condition() {
    while true; do
        (
            flock -s 200  # 获取共享锁
            if [ -f "$LOCK_FILE" ]; then
                content=$(cat "$LOCK_FILE")
                echo "条件已满足: $content"
                exit 0
            fi
        ) 200>"$LOCK_FILE.lock"
        sleep 0.1
    done
}
set_condition &
wait_condition &
wait

使用trap和信号

#!/bin/bash
# 条件变量
ready=0
# 信号处理函数
handler() {
    ready=1
    echo "接收到信号,条件满足"
}
# 设置信号处理
trap handler USR1
# 条件等待函数
wait_for_condition() {
    echo "等待条件..."
    while [ $ready -eq 0 ]; do
        sleep 0.1
    done
    echo "条件满足,继续执行"
}
# 条件设置函数
set_condition() {
    echo "设置条件..."
    sleep 2
    kill -USR1 $$  # 发送信号给自己
}
# 启动
set_condition &
wait_for_condition &
wait

复合条件变量(使用coproc进程替代)

#!/bin/bash
# 使用协同进程
coproc CONDITION {
    while true; do
        read cmd
        case $cmd in
            "wait")
                echo "waiting" >&2
                ;;
            "notify")
                echo "notified" >&2
                ;;
            "exit")
                break
                ;;
        esac
    done
}
# 等待条件
wait_condition() {
    echo "wait" >&${CONDITION[1]}
}
# 通知条件
notify_condition() {
    echo "notify" >&${CONDITION[1]}
}
# 主逻辑
{
    wait_condition
    echo "条件等待中..."
    sleep 2
    notify_condition
    echo "条件满足"
}
exec {CONDITION[1]}>&-
kill $CONDITION_PID 2>/dev/null

最佳实践建议

  1. 避免忙等待:使用sleepread等阻塞操作
  2. 清理资源:确保信号文件、FIFO等在退出时被清理
  3. 使用临时文件:将信号文件放在/tmp目录,使用避免冲突
  4. 设置超时:防止永久阻塞
# 带超时的条件等待
wait_with_timeout() {
    local timeout=$1
    local signal_file=$2
    local start_time=$(date +%s)
    while [ ! -f "$signal_file" ]; do
        sleep 0.1
        local current_time=$(date +%s)
        if [ $((current_time - start_time)) -ge $timeout ]; then
            echo "超时等待"
            return 1
        fi
    done
    return 0
}

选择哪种方式取决于具体需求:

  • 简单场景:文件信号即可
  • 生产者-消费者:FIFO命名管道更合适
  • 需要互斥访问flock文件锁
  • 进程间通信:信号机制

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