我不知道一般如何解释这个问题,所以我就用这个例子:
#!/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
Run Code Online (Sandbox Code Playgroud)
现在我希望每个读取线程都占用一行(或几行),但第一次读取过程将占用所有行(以我不理解的随机顺序,但没关系),将其放在某个缓冲区中并且所有其他读取过程都不会得到任何一行。
此外,提供给读取命令的超时参数似乎不起作用,因为阅读器 2-10 没有退出。
read
超时read
超时实际上有效。这里的问题是在读取模式下打开 FIFO 会阻塞,直到 FIFO 在写入模式下打开。在这种情况下,这不是read
被阻止,而是bash
在将 FIFO 重定向到标准输入时。
一旦其他进程打开 FIFO 进行写入,bash
将成功打开 FIFO 进行读取并执行read
命令(这将按预期超时)。
如果您使用的是 Linux,fifo的手册页告诉我们“打开 FIFO 进行读写将在阻塞和非阻塞模式下都成功”。因此,即使没有其他进程打开 FIFO 进行写入,以下命令也会超时:
read -st 1 data <> "$fifo"
Run Code Online (Sandbox Code Playgroud)
一旦您的 shell 进程打开 FIFO 进行读取,写入器将被解锁,并且当bash
将 FIFO 重定向到 stdin 并调用时read
,写入器可能能够打开 FIFO 并多次写入其中。由于您一次仅读取一行,因此在两端 FIFO 关闭时剩余的任何要读取的行都将丢失。更好的解决方案是通过将 FIFO 重定向到整个while
...done
循环的stdin 来保持 FIFO 打开,就像您对 fd 3 所做的那样。类似于:
while ...; do
...
read -st 1 data
...
done 3<"$lock" < "$fifo"
Run Code Online (Sandbox Code Playgroud)
或者甚至在更高级别,如果您有多个读者并行。重要的是保持 FIFO 打开。作家方面也是如此。
例如,使用您随更新发布的代码,上层将是:
# Writer
writer > "$myfifo" &
# Reader
for i in $(seq 1 10); do
reader $i &
sleep 1
done < "$myfifo"
Run Code Online (Sandbox Code Playgroud)
当然,删除$myfifo
代码中其他任何地方的重定向,并删除编写器echo "$(date -R) writing \"$i\"."
中的 ,或将其重定向到 stderr,否则它会进入 FIFO。
归档时间: |
|
查看次数: |
3309 次 |
最近记录: |