Я запускаю этот цикл, чтобы проверять и печатать некоторые вещи каждую секунду. Однако, поскольку вычисления занимают, возможно, несколько сотен миллисекунд, время печати иногда пропускается за секунду.
Есть ли способ написать такой цикл, чтобы я гарантированно получал распечатку каждую секунду? (При условии, конечно, что вычисления в цикле занимают меньше секунды :))
while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' $TIME $FOO $BAR
sleep 1
done
bash
timestamps
sleep
forthrin
источник
источник
sched(7)
API (POSIX: смотрите<sched.h>
и страницы, ссылки на которые есть), вы в принципе не можете иметь гарантии в реальном времени для этой формы.Ответы:
Чтобы остаться немного ближе к исходному коду, я делаю так:
Это немного меняет семантику: если ваш материал занял меньше секунды, он просто будет ждать, пока пройдет полная секунда. Однако, если ваш материал по какой-либо причине занимает больше секунды, он не будет порождать даже больше подпроцессов, и это никогда не закончится.
Таким образом, ваши вещи никогда не работают параллельно, а не в фоновом режиме, поэтому переменные тоже работают как положено.
Обратите внимание, что если вы также запустите дополнительные фоновые задачи, вам придется изменить
wait
инструкцию, чтобы только ждатьsleep
конкретного процесса.Если вам нужно, чтобы он был еще более точным, вам, скорее всего, придется просто синхронизировать его с системными часами и мс сна вместо полных секунд.
Как синхронизировать системные часы? Понятия не имею, глупая попытка
По умолчанию:
Выход: 003511461 010510925 016081282 021643477 028504349 03 ... (продолжает расти)
синхронизированные:
Выход: 002648691 001098397 002514348 001293023 001679137 00 ... (остается тем же)
источник
sleep
обрабатывают доли секунды?sleep 0.9 || sleep 1
неверный параметр - практически единственная причина, по которой сон никогда не срабатывает.sleep 0.9
его будут интерпретировать какsleep 0
наивные реализации (учитывая, что этоatoi
будет делать). Не уверен, что это приведет к ошибке.gdate
date +%N
Если вы можете перестроить свой цикл в сценарий / Oneliner то самый простой способ сделать это с
watch
и егоprecise
вариант.Вы можете увидеть эффект с помощью
watch -n 1 sleep 0.5
- он покажет количество секунд, но иногда пропускает секунду. Запуск его aswatch -n 1 -p sleep 0.5
выдаст два раза в секунду, каждую секунду, и вы не увидите пропусков.источник
Выполнение операций в подоболочке, выполняемой как фоновое задание, сделает их не так сильно мешающими
sleep
.Единственное время, «украденное» из одной секунды, - это время, затрачиваемое на запуск подоболочки, поэтому в конечном итоге она пропускает секунду, но, надеюсь, реже, чем исходный код.
Если код в подоболочке заканчивается более чем на секунду, цикл начинает накапливать фоновые задания и в конечном итоге исчерпывает ресурсы.
источник
Другой альтернативой (если вы не можете использовать, например,
watch -p
как предлагает Maelstrom) являетсяsleepenh
[ manpage ], которая предназначена для этого.Пример:
Обратите внимание на
sleep 0.2
то, что симулятор выполняет некоторое трудоемкое задание, съедая около 200 мс. Несмотря на это, выход наносекунд остается стабильным (ну, по стандартам ОС не в реальном времени) - это происходит раз в секунду:Это меньше 1 мс, и никаких тенденций. Это довольно хорошо; Вы должны ожидать отказов не менее 10 мс, если в системе есть какая-либо нагрузка, но со временем она не будет дрейфовать. Т.е. вы не потеряете ни секунды.
источник
С
zsh
:Если ваш сон не поддерживает с плавающей точкой секунд, вы можете использовать
zsh
«Szselect
вместо (послеzmodload zsh/zselect
):Они не должны дрейфовать до тех пор, пока команды в цикле будут выполняться менее одной секунды.
источник
У меня было точно такое же требование для сценария оболочки POSIX, где все помощники (usleep, GNUsleep, sleepenh, ...) недоступны.
см .: https://stackoverflow.com/a/54494216
источник