У меня есть куча изображений PNG в каталоге. У меня есть приложение pngout, которое я запускаю для сжатия этих изображений. Это приложение вызывается скриптом, который я сделал. Проблема в том, что этот скрипт делает по одному за раз, что-то вроде этого:
FILES=(./*.png)
for f in "${FILES[@]}"
do
echo "Processing $f file..."
# take action on each file. $f store current file name
./pngout -s0 $f R${f/\.\//}
done
Обработка только одного файла за раз занимает много времени. После запуска этого приложения я вижу, что загрузка ЦП составляет всего 10%. Поэтому я обнаружил, что могу разделить эти файлы на 4 пакета, поместить каждый пакет в каталог и запустить 4 из четырех окон терминала, четыре процесса, так что у меня есть четыре экземпляра моего скрипта, одновременно обрабатывающие эти изображения, и работа занимает 1/4 времени.
Вторая проблема заключается в том, что я потерял время на разделение образов и пакетов, копирование скрипта в четыре каталога, открытие 4 окон терминала, бла-бла...
Как это сделать с помощью одного сценария, не разделяя ничего?
Я имею в виду две вещи: во-первых, как мне из скрипта bash запустить процесс в фоновом режиме? (просто добавить & в конец?) Во-вторых: как мне прекратить отправку задач в фоновом режиме после отправки четвертой задачи и заставить скрипт ждать, пока задачи не закончатся? Я имею в виду, просто отправлять новую задачу в фоновом режиме по мере завершения одной задачи, всегда поддерживая 4 задачи параллельно? Если я этого не сделаю, цикл запустит миллионы задач в фоновом режиме, и процессор будет загружен.
решение1
Если у вас есть копия , xargs
которая поддерживает параллельное выполнение с -P
, вы можете просто сделать
printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}
Для других идей, Wooledge Bash wiki имеетразделв статье «Управление процессами», описывающей именно то, что вам нужно.
решение2
В дополнение к уже предложенным решениям вы можете создать makefile, который описывает, как сделать сжатый файл из несжатого, и использовать его make -j 4
для параллельного запуска 4 заданий. Проблема в том, что вам нужно будет называть сжатые и несжатые файлы по-разному или хранить их в разных каталогах, иначе написать разумное правило make будет невозможно.
решение3
Если у вас есть GNU Parallelhttp://www.gnu.org/software/parallel/установлен, вы можете сделать это:
parallel ./pngout -s0 {} R{} ::: *.png
Вы можете установить GNU Parallel просто:
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
Посмотрите ознакомительные видеоролики по GNU Parallel, чтобы узнать больше: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
решение4
Отвечая на ваши два вопроса:
- да, добавление & в конце строки даст команду оболочке запустить фоновый процесс.
- Используя эту
wait
команду, вы можете указать оболочке дождаться завершения всех фоновых процессов, прежде чем продолжить работу.
Вот скрипт, измененный так, чтобы j
он использовался для отслеживания количества фоновых процессов. Когда NB_CONCURRENT_PROCESSES
он достигнут, скрипт сбросится j
до 0 и будет ждать завершения всех фоновых процессов, прежде чем возобновит свое выполнение.
files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
echo "Processing $f file..."
# take action on each file. $f store current file name
./pngout -s0 "$f" R"${f/\.\//}" &
((++j == nb_concurrent_processes)) && { j=0; wait; }
done