読み取りタイムアウトパラメータ(-t)が機能しませんか?

読み取りタイムアウトパラメータ(-t)が機能しませんか?

この問題を説明する方法がわからないので、次の例を使用します。

#!/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

これで、読み取りスレッドはそれぞれ1行(または数行)を読み取りたいと思いますが、最初の読み取りプロセスはすべての行を読み取ります(ランダムな順序で理解していないが重要ではありません)。バッファのどこかにあり、他のすべての読み取りプロセスは行を取得しません。

また、Reader 2-10は終了しないため、Readコマンドに指定されたタイムアウトパラメータが機能しないようです。

  1. なぜ?
  2. 行が読者間で(ある程度)均等に分配されるように、この問題をどのように解決できますか?

答え1

readタイムアウトを許可

readタイムアウトが機能します。ここでの問題は、FIFOが書き込みモードで開かれるまで、読み取りモードでFIFOを開くことがブロックされることです。この場合、readブロックするのではなく、bashFIFOがstdinにリダイレクトされている間はブロックします。

他のプロセスが書き込み用にFIFOを開くと、読み取り用にbashFIFOが正常に開き、readコマンドが実行されます(予想どおりにタイムアウトしました)。

Linuxを使用している場合は、fifo のマニュアルページ「読み取りと書き込みのためにFIFOを開くことは、ブロックモードと非ブロックモードの両方で成功します」と伝えます。したがって、他のプロセスに書き込むために開いているFIFOがない場合でも、次のコマンドはタイムアウトします。

read -st 1 data <> "$fifo"

競争条件に注意してください。

シェルプロセスが読み取り用にFIFOを開くと、作成者のロックが解除され、FIFOがstdinにリダイレクトされて呼び出されるbashと、作成者readはFIFOを開き、ここに書き込むことができます。何度も。一度に 1 行しか読み取ることができないため、FIFO の両端を閉じると、読み取る残りの行が失われます。より良い解決策は、fd 3で行ったように、whileループ全体でstdinにリダイレクトしてFIFOを開いたままにすることです。done次のように:

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に移動します。

関連情報