Вхождение в состояние гонки, даже с ожиданием
Я сталкиваюсь со странным состоянием гонки в моем bash
программа. Я попытался воспроизвести его с помощью достаточно простой демонстрационной программы, но, очевидно, что это верно для всех / большинства попыток демонстрации гонки, связанных со временем, я не смог.
Вот абстрактная версия программы, которая НЕ дублирует проблему, но позвольте мне все же объяснить:
# Abstracted version of the original program
# that is NOT able to demo the race.
#
function foo() {
local instance=$1
# [A lot of logic here -
# all foreground commands, nothing in the background.]
echo "$instance: test" > /tmp/foo.$instance.log
echo "Instance $instance ended"
}
# Launch the process in background...
#
echo "Launching instance 1"
foo 1 &
# ... and wait for it to complete.
#
echo "Waiting..."
wait
echo "Waiting... done. (wait exited with: $?)"
# This ls command ALWAYS fails in the real
# program in the 1st while-iteration, complaining about
# missing files, but works in the 2nd iteration!
#
# It always works in the very 1st while-iteration of the
# abstracted version.
#
while ! ls -l /tmp/foo.*; do
:
done
В моей оригинальной программе (а НЕ в вышеупомянутой абстрактной версии) я вижу Waiting... done. (wait exited with: 0)
на стандартный вывод, как я вижу в приведенной выше версии. Тем не менее, ls -l
всегда терпит неудачу в оригинале, но всегда работает в вышеупомянутой абстрактной версии в самом первом while
итерация цикла
Так же ls
Команда терпит неудачу, несмотря на видение Instance 1 ended
сообщение на стандартный вывод. Выход:
$ ./myProgram
Launching instance 1
Waiting...
Waiting... done. (wait exited with: 0)
Instance 1 ended
ls: cannot access '/tmp/foo.*': No such file or directory
/tmp/foo.1
$
Я заметил, что с циклом while можно безопасно покончить, если я поставлю sleep 1
прямо перед ls
в моей оригинальной программе вот так:
# This too works in the original program:
sleep 1
ls -l /tmp/foo.*
Вопрос: почему нет wait
работа как положено в моей оригинальной программе? Любые предложения, чтобы хотя бы помочь устранить проблему?
я использую bash 4.4.19
на Ubuntu 18.04.
РЕДАКТИРОВАТЬ: я только что подтвердил, что звонок wait
в исходном исходящая программа завершается с кодом состояния 0
,
РЕДАКТИРОВАТЬ 2: не должен Instance 1 ended
сообщение появляется ДО Waiting... done. (wait exited with: 0)
? Может ли это быть проблемой сброса дискового буфера / кэша ОС при работе с фоновыми процессами в bash?
РЕДАКТИРОВАТЬ 3: Если вместо while
петля или sleep 1
хаки, я выдаю sync
команда, тогда, вуаля, это работает! Но почему я должен сделать sync
в одной программе, а в другой?
1 ответ
Я заметил, что каждый из следующих трех хаков работает, но не совсем уверен, почему:
Хак 1
while ! ls -l /tmp/foo.*; do
:
done
Хак 2
sleep 1
ls -l /tmp/foo.*
Хак 3
sync
ls -l /tmp/foo.*
Может ли это быть проблемой сброса дискового буфера / кэша ОС, особенно при работе с фоновыми процессами, особенно в bash
? Другими словами, призыв к wait
кажется, возвращается, ДО того, как он очищает дисковый кэш (или, ДО ОС, сам понимает и, завершает очистку дискового кэша).
РЕДАКТИРОВАТЬ Благодаря @Jon, его было очень близкое предположение и заставило меня думать в правильном направлении, наряду с вековым, немного мудрым советом по настройке от @chepner.
Настоящая проблема: я начинал foo
не прямо / явно, как показано в моей неточной абстрактной версии в моем первоначальном вопросе, а через другой launchThread
функция, которая, после некоторой бухгалтерии, также сказала бы foo 1 &
в его теле. И призыв к launchThread
был сам по себе с суффиксом &
! Так что мой wait
действительно ждал launchThread
и не на foo
! sleep
, sync
, а также while
просто помогали выиграть больше времени для foo
чтобы завершить, поэтому введение их работало. Ниже приведена более точная демонстрация проблемы, даже если вы можете или не сможете скопировать ее в своей собственной системе (из-за различий в расписании / синхронизации в разных системах):
#!/bin/bash -u
function now() {
date +'%Y-%m-%d %H:%M:%S'
}
function log() {
echo "$(now) - $@" >> $logDir/log # Line 1
}
function foo() {
local msg=$1
log "$msg"
echo " foo ended"
}
function launchThread() {
local f=$1
shift
"$f" "$@" & # Line 2
}
logDir=/tmp/log
/bin/rm -rf "$logDir"
mkdir -p "$logDir"
echo "Launching foo..."
launchThread foo 'message abc' & # Line 3
echo "Waiting for foo to finish..."
wait
echo "Waiting for foo to finish... done. (wait exited with: $?)"
ls "$logDir"/log*
Вывод вышеуказанной глючной программы:
Launching foo...
Waiting for foo to finish...
Waiting for foo to finish... done. (wait exited with: 0)
foo ended
ls: cannot access '/tmp/log/log*': No such file or directory
Если я удалю &
от ЛИБО Line 2
ИЛИ от Line 3
программа работает корректно со следующими данными:
Launching foo...
Waiting for foo to finish...
foo ended
Waiting for foo to finish... done. (wait exited with: 0)
/tmp/log/log
Программа также работает правильно, если я удаляю $(now)
часть от Line 1
,