本文目录导读:

在Shell脚本中,条件变量同步通常通过以下机制实现:
使用wait和notify(通过文件信号)
简单文件锁方式
#!/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
最佳实践建议
- 避免忙等待:使用
sleep或read等阻塞操作 - 清理资源:确保信号文件、FIFO等在退出时被清理
- 使用临时文件:将信号文件放在
/tmp目录,使用避免冲突 - 设置超时:防止永久阻塞
# 带超时的条件等待
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文件锁 - 进程间通信:信号机制