我不确定如何解释这个问题,所以我只用这个例子:
#!/bin/bash
cleanup() {
rm "$myfifo"
rm "$mylock"
kill '$(jobs -p)'
}
writer() {
for i in $(seq 0 100); do
echo "$(date -R) writing \"$i\"."
echo "$i" > "$myfifo"
done
}
reader() {
while true; do
flock 3
read -st 1 line
status=$?
if [ $status -eq 0 ]; then
echo "$(date -R) reading \"$line\" in thread $1."
else
echo "$(date -R) status $status in thread $1.
break
fi
flock -u 3
sleep 10
done 3<"$mylock" <"$myfifo"
}
trap cleanup EXIT
myfifo="$(mktemp)"
mylock="$(mktemp)"
rm "$myfifo"
mkfifo "$myfifo"
writer &
for i in $(seq 1 10); do
reader $i &
sleep 1
done
wait
现在我希望读取线程每个读取一行(或几行),但第一个读取过程将读取所有行(以随机顺序,我不明白,但这没关系),将其放在缓冲区中的某个地方并且所有其他读取进程都不会得到任何行。
此外,提供给读取命令的超时参数似乎不起作用,因为读取器 2-10 不退出。
- 为什么?
- 我该如何解决这个问题,以便这些行在读者之间(在某种程度上)均匀分布?
答案1
允许read
超时
read
timeout 确实有效。这里的问题是,以读取模式打开 FIFO 会阻塞,直到以写入模式打开 FIFO。在这种情况下,这不是read
阻塞,而是bash
在将 FIFO 重定向到 stdin 时阻塞。
一旦其他进程打开 FIFO 进行写入,bash
将成功打开 FIFO 进行读取并执行read
命令(将按预期超时)。
如果您使用的是 Linux,fifo 的手册页告诉我们“打开 FIFO 进行读取和写入在阻塞和非阻塞模式下都会成功”。因此,即使没有其他进程打开 FIFO 进行写入,以下命令也会超时:
read -st 1 data <> "$fifo"
注意竞争条件
一旦您的 shell 进程打开 FIFO 进行读取,写入器就会被解锁,并且当bash
将 FIFO 重定向到 stdin 并调用时read
,写入器可能能够打开 FIFO 并写入其中几次。由于一次只能读取一行,因此当 FIFO 两端都关闭时,任何剩余要读取的行都将丢失。更好的解决方案是在整个while
...循环中将 FIFO 重定向到 stdin 来保持 FIFO 打开done
,就像对 fd 3 所做的那样。如下所示:
while ...; do
...
read -st 1 data
...
done 3<"$lock" < "$fifo"
或者甚至在上层,如果您有多个并行读者。重要的是保持 FIFO 打开。对于作家来说也是如此。
例如,使用您随更新发布的代码,上层将是:
# Writer
writer > "$myfifo" &
# Reader
for i in $(seq 1 10); do
reader $i &
sleep 1
done < "$myfifo"
当然,请删除$myfifo
代码中其他位置的重定向,并删除echo "$(date -R) writing \"$i\"."
编写器中的重定向,或将其重定向到 stderr,否则它将进入 FIFO。