Паралельно чотири завдання ... як це зробити?


23

У мене в каталозі є маса зображень 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 завдання паралельно? якщо я цього не роблю, цикл виводить на задній план мільйони завдань, і процесор забиється.


Відповіді:


33

Якщо у вас є копія, xargsяка підтримує паралельне виконання -P, ви можете просто зробити

printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}

Щодо інших ідей, у вікі Wooledge Bash є розділ у статті «Управління процесами», який описує саме те, що ви хочете.


2
Існують також "gnu паралельні" та "xjobs", призначені для цього випадку. В основному це питання смаку, який ви віддаєте перевагу.
wnoise

Чи можете ви поясніть запропоновану команду? Спасибі!
Євген S

1
@EugeneS Не могли б ви бути трохи більш конкретними щодо тієї частини? Printf збирає всі png-файли та передає їх по трубі до xargs, який збирає аргументи зі стандартного введення та об'єднує їх у аргументи для pngoutкоманди, яку хотів виконати ОП. Ключовим варіантом є те -P 4, що вказує xargs використовувати до 4 одночасних команд.
jw013

2
Вибачте за неточність. Мене конкретно цікавило, чому ти printfтут використовував функцію, а не просто регулярну ls .. | grep .. *.png? Також мене зацікавили xargsпараметри, які ви використовували ( -0і -I{}). Спасибі!
Євген S

3
@EugeneS Це для максимальної коректності та надійності. Імена файлів не є рядками, і lsїх не можна використовувати для розбору імен файлів портативно та безпечно . Тільки безпечні символи використовувати , щоб розмежувати імена файлів \0і /, так як будь-який інший характер, в тому числі \n, може бути частиною самого імені файлу. В printfвикористовує \0для імен файлів розмежувати, і -0інформує xargsпро це. -I{}Каже xargsзамінити {}з аргументом.
jw013

8

На додаток до вже запропонованих рішень, ви можете створити файл файлів, який описує, як зробити стислий файл з нестисненого, і використовувати make -j 4для запуску 4 завдань паралельно. Проблема полягає в тому, що вам потрібно буде по-різному називати стислі та нестиснені файли або зберігати їх у різних каталогах, інакше написання розумного правила make буде неможливим.


7

Якщо у вас встановлений GNU Parallel http://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


5

Щоб відповісти на два питання:

  • так, додавання & в кінці рядка доручить оболонці запустити фоновий процес.
  • використовуючи 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

1
Це буде чекати останнього з чотирьох паралельних процесів, а потім розпочне набір ще чотирьох. Можливо, варто створити масив з чотирьох PID, а потім чекати цих конкретних PID?
Нілс

Просто для пояснення моїх виправлень до коду: (1) Що стосується стилю, уникайте всіх великих імен змінних, оскільки вони потенційно можуть суперечити внутрішнім змінним оболонок. (2) Додано котирування $fтощо. (3) Використовуйте [для POSIX-сумісних сценаріїв, але для чистого башу [[завжди бажано. У цьому випадку ((більше підходить арифметика.
jw013
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.