Запись из нескольких процессов, запущенных через xargs, в один и тот же конвейер FIFO приводит к пропуску строк

У меня есть сценарий, в котором я распараллеливаю выполнение заданий, отслеживая прогресс. Я делаю это с помощью xargs и именованного канала FIFO. Моя проблема в том, что я, пока xargs работает хорошо, некоторые строки, записанные в канал, теряются. Есть идеи, в чем проблема?

Например, следующий сценарий (в основном мой сценарий с фиктивными данными) выдаст следующий результат и зависнет в конце, ожидая этих недостающих строк:

$ bash test2.sh 
Progress: 0 of 99
DEBUG: Processed data 0 in separate process
Progress: 1 of 99
DEBUG: Processed data 1 in separate process
Progress: 2 of 99
DEBUG: Processed data 2 in separate process
Progress: 3 of 99
DEBUG: Processed data 3 in separate process
Progress: 4 of 99
DEBUG: Processed data 4 in separate process
Progress: 5 of 99
DEBUG: Processed data 5 in separate process
DEBUG: Processed data 6 in separate process
DEBUG: Processed data 7 in separate process
DEBUG: Processed data 8 in separate process
Progress: 6 of 99
DEBUG: Processed data 9 in separate process
Progress: 7 of 99
##### Script is hanging here (Could happen for any line) #####
#!/bin/bash
clear

printStateInLoop() {
  local pipe="$1"
  local total="$2"
  local finished=0

  echo "Progress: $finished of $total"
  while true; do
    if [ $finished -ge $total ]; then
      break
    fi

    let finished++
    read line <"$pipe"
      # In final script I would need to do more than just logging
    echo "Progress: $finished of $total"
  done
}

processData() {
  local number=$1
  local pipe=$2

  sleep 1 # Work needs time
  echo "$number" >"$pipe"
  echo "DEBUG: Processed data $number in separate process"
}
export -f processData

process() {
  TMP_DIR=$(mktemp -d)
  PROGRESS_PIPE="$TMP_DIR/progress-pipe"
  mkfifo "$PROGRESS_PIPE"

  DATA_VECTOR=($(seq 0 1 99)) # A bunch of data
  printf '%s\0' "${DATA_VECTOR[@]}" | xargs -0 --max-args=1 --max-procs=5 -I {} bash -c "processData \$@ \"$PROGRESS_PIPE\"" _ {} &

  printStateInLoop "$PROGRESS_PIPE" ${#DATA_VECTOR[@]}
}

process
rm -Rf "$TMP_DIR"

В другом сообщении я получил предложение переключиться на while read line; do … done < "$pipe" (функция ниже) вместо while true; do … read line < "$pipe" … done, чтобы не закрывать конвейер при каждой прочитанной строке. Это снижает частоту возникновения проблемы, но все же она возникает: некоторые строки отсутствуют, а иногда и xargs: bash: terminated by signal 13.

printStateInLoop() {
  local pipe="$1"
  local total="$2"
  local finished=0

  echo "Progress: $finished of $total"
  while [ $finished -lt $total ]; do
    while read line; do
      let finished++
      # In final script I would need to do more than just logging
      echo "Progress: $finished of $total"
    done <"$pipe"
  done
}

Многие люди в SO предлагали использовать parallel или pv для этого. К сожалению, эти инструменты недоступны на очень ограниченной целевой платформе. Вместо этого мой сценарий основан на xargs.


person Sebastian Barth    schedule 08.11.2020    source источник
comment
Рассматривали ли вы, чтобы каждый писатель получил блокировку канала перед записью в канал? поиск в Google по bash linux flock write pipe дает несколько подсказок, включая многообещающий ответ на FIFO с одним ЧИТАТЕЛЕМ и несколькими ПИСАТЕЛЯМИ в BASH   -  person markp-fuso    schedule 09.11.2020
comment
Нет блокировки при записи в трубу. Вполне возможные события будут перезаписаны. Может нужно блокировать процессы (стаять?). Или напишите каждую в отдельную трубу и соберите результаты   -  person Dale    schedule 09.11.2020
comment
Спасибо. Это действительно решило проблему. Я уже искал механизм блокировки файлов, но, очевидно, не использовал правильные условия поиска.   -  person Sebastian Barth    schedule 09.11.2020


Ответы (1)


Решение (как указано @ markp-fuso и @Dale) заключалось в создании блокировки файла.

Вместо того:

echo "$number" >"$pipe"

Теперь я использую flock, чтобы сначала создать / дождаться блокировки:

flock "$pipe.lock" echo "$number" >"$pipe"
person Sebastian Barth    schedule 08.11.2020