параметр таймаута чтения (-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 

Теперь я ожидал бы, что потоки чтения будут брать строки (или несколько строк), но первый процесс чтения будет принимать все строки (в случайном порядке, который я не понимаю, но это нормально), поместите его в буфер где-нибудь и все остальные процессы чтения не получат никакой линии.

Также параметр тайм-аута, предоставленный команде чтения, не работает, потому что считыватели 2-10 не выходят.

  1. Зачем?
  2. Как я могу это исправить, чтобы линии получали (несколько) равномерно распределенные среди читателей?

Предоставление таймаута

таймаут read фактически работает. Проблема здесь в том, что открытие FIFO в блоках режима чтения до тех пор, пока FIFO не откроется в режиме записи. И в этом случае это не считается заблокированным, это bash , когда перенаправление вашего FIFO на stdin.

Как только какой-либо другой процесс открывает FIFO для записи, bash успешно откроет FIFO для чтения и выполнит команду read (которая будет таймаутом, как ожидалось).

Если вы используете Linux, man-страница для fifo говорит нам, что «открытие FIFO для чтения и записи будет успешным как в режиме блокировки, так и в режиме неблокирования». Поэтому следующая команда будет отключена, даже если ни один другой процесс не откроет FIFO для записи:

 read -st 1 data <> "$fifo" 

Остерегайтесь состояния гонки

После того, как ваш процесс оболочки откроет FIFO для чтения, автор (ы) будет разблокирован и, к тому времени, когда bash перенаправляет FIFO на stdin и вызывает read , писатель может открыть FIFO и записать его несколько раз . Поскольку вы читаете только одну строку за раз, любая строка, оставшаяся для чтения, когда FIFO закрыта с обоих концов, будет потеряна. Лучшим решением было бы сохранить FIFO открытым, перенаправив его на stdin для всего whiledone loop, как вы сделали для 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.