Четыре задачи параллельно... как это сделать?

Четыре задачи параллельно... как это сделать?

У меня есть куча изображений 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

Связанный контент