Bash threading: ждать завершения всех заданий, не работает?
Я пишу немного script, который будет создавать архивы в основном потоке и после завершения каждого архива будет создан новый поток, вызывая функцию, которая будет заботиться о загрузке этих архивов. Причина, по которой я хочу, чтобы загрузка выполнялась в фоновом режиме, заключается в создании другого архива при загрузке предыдущих архивов.
Проблема, с которой я столкнулась, находится в самом конце script. То есть, основной поток не дожидался завершения всех потоков загрузки до выхода. Посмотрите на следующий упрощенный script (я удалил/изменил части кода, не связанные с проблемой)
function func {
for files in /home/somewhere/
do
echo "Uploading $1" &
done
wait
}
find /home/some/path -type f | while read filename ; do
echo "Creating archive of $filename"
func $somevariable &
done
wait
Все выполняется очень красиво до тех пор, пока не будет создан последний архив, а затем script заканчивается перед тем, как закончится нить func
, оставив много файлов не загруженным.
Спасибо за ваши идеи.
Ответы
Ответ 1
Обновление: хорошие комментарии в комментарии.
Итак, при втором взгляде оказывается, что проблема - это подоболочка, которая создается каналом в цикле. Это хороший способ структурировать script, но вам нужно сделать окончательное ожидание в оболочке, которая выделяет фоновые задачи.
Итак, сделайте что-нибудь вроде этого:
find /home/some/path -type f | (while read filename; do
echo "Creating archive of $filename"
func $somevariable &
done
wait
)
Ответ 2
Tricky! Проблема в том, что этот блок
find /home/some/path -type f | while read filename ; do
...
done
Создает подоболочку. В этой подоболочке создаются рабочие переменные func $. Родительская оболочка видит, что все фоновые задания, которые она создала, закончились, она не отслеживает фоновые задания, созданные подсветами, которые она породила.
Самое простое исправление - создать вместо этого исходные задания из родительской оболочки. Вы можете избежать создания подоболочки, не используя трубку:
while read filename ; do
...
done < <(find /home/some/path -type f)
Ну, это создает подоболочку --- для поиска --- но блок while больше не находится в подоболочке.
Обратите внимание, что вышеизложенное работает только под bash. (Не знаю о ksh или zsh, возможно, он работает и там, но он не будет работать под золой и другими производными.)
Ответ 3
Если вы выполняете wait
без аргументов, он должен ждать завершения активных дочерних процессов.
Вероятно, проблема заключается в том, что "все активные в настоящее время дочерние процессы" не означает, что вы думаете, что это означает в этом контексте. В частности, если вы создаете конвейеры в подоболочке, не совсем ясно, будут ли они ожидаться в родительской оболочке.
Я подозреваю, что wait
фактически ожидает только процессов/конвейеров, которые отображаются на выходе jobs
. Попробуйте несколько экспериментов...
Возможной альтернативой может быть захват идентификаторов дочерних процессов и вызов wait n
для каждого идентификатора.
Ответ 4
Вы можете попробовать этот скрипт. Это именно так. https://github.com/pabloniklas/BASH/blob/master/lib_cpu.sh
Ответ 5
Вы можете зацикливаться до тех пор, пока команда jobs
ничего не возвращает в качестве альтернативного метода.