#bash #file #locking #atomic
#bash #файл #блокировка #атомарный
Вопрос:
Я хочу использовать текстовый файл в качестве очереди задач для моего приложения (для записи в базу данных, хотя это не имеет значения), где:
- Я добавляю элемент в очередь через
echo "some task" >> task_queue.txt
- Я удаляю элемент из очереди атомарно
Для 2. Я не знаю способа избежать условий гонки, если я обращаюсь / изменяю task_queue.txt
в нескольких потоках или процессах. Следующее не является атомарным:
ITEM=`head -1 task_queue.txt`
sed -i '1d' task_queue.txt
# process the item in the application
Предоставляет ли Bash более элегантный способ сделать это, чем использование файла блокировки? Я никогда flock
раньше не использовал, поэтому я не знаю, беспорядочно ли это (например, когда обработка задач моего приложения завершается с ошибкой).
Ответ №1:
Все они страдают от состояния гонки, между проверкой, осуществляется ли доступ к файлу, и выполнением операций с файлом. Хотя предположительно время между проверкой и операцией будет очень небольшим.
Одним из способов может быть использование pgrep
-f
опции для сопоставления с полной командной строкой и сопоставления, если файл соответствует, т.Е. Если какой-либо процесс обращается к этому файлу. Это предполагает, что процесс не изменяет свою командную строку.
Это может сделать:
if ! pgrep -f task_queue.txt amp;>/dev/null; then
## File not Open, do stuff
else
## File is Open, do stuff
fi
Другой подход будет включать синтаксический lsof
анализ или fuser
(это то же самое, что и синтаксический /proc/PID/fd/*
анализ):
if ! lsof /path/to/task_queue.txt amp;>/dev/null; then
## File not Open, do stuff
else
## File is Open, do stuff
fi
Аналогично fuser
:
if ! fuser /path/to/task_queue.txt amp;>/dev/null; then
## File not Open, do stuff
else
## File is Open, do stuff
fi
Обратите внимание, что здесь мы отправляем как STDOUT, так и STDERR из lsof
/ fuser
to /dev/null
, это может быть не всегда желательно, так как может быть какое-то предупреждение / ошибка, и поскольку мы зависим только от статуса выхода, все они будут неправильно обрабатываться при использовании файла. Это было бы проще реализовать, если lsof
/ fuser
имеет разный статус выхода для разных событий, но все, что я вижу, — это 1
для каждого вида сбоя или отсутствия совпадения.
Комментарии:
1. Интересный подход, о котором я не знал
pgrep -f
. Интересно, лучше ли это, чем использовать flock. Я предполагаю, что мне придется использовать оба варианта одновременно и посмотреть, в каком из них я чувствую себя более уверенно.2. Хммм, на самом деле, разве не возможно, что «материал» в
if
теле не удерживается в файловом дескрипторе все время? 2 команды могут иметь небольшой промежуток времени, между которыми файл не открыт, что может позволить другому процессу украсть его.3. @Sridhar-Sarnobat Очевидно, что существует вероятность состояния гонки между
pgrep
и операцией с файлом, какой бы маленькой она ни была. Если вам нужна точность, вам следует посмотреть на механизм блокировки, напримерflock
.4. Да, я думаю. Хотя после
pgrep
— это не самая большая моя забота. Большее беспокойство вызывают мои многочисленные команды, которые следуют за этим. Но я ценю ваши предложения и примеры кода.5. @Sridhar-Sarnobat Тогда вы можете (возможно) использовать
while
конструкцию и запускать одну команду за раз (если это возможно).
Ответ №2:
Хотя я не могу подтвердить, что это атомарно, это намного более атомарно, чем все, что я могу написать сам (вероятно), и достаточно хорошо для моего приложения, не являющегося критически важным:
sed -i -e '1 w /dev/stdout' -e '1d' task_queue.txt
(кредиты: https://unix.stackexchange.com/a/108479/7000 )