Выполнение в фоновом режиме, но ограничение количества выполнений

У меня есть программа, которая выполняет некоторые операции с указанным файлом журнала, сбрасывая на диск несколько раз для каждого выполнения. Я вызываю эту программу из Perl-скрипта, который позволяет указать каталог, и запускаю эту программу для всех файлов в этом каталоге. Это может занять много времени из-за всех флешей.

Я хотел бы выполнить программу и запустить ее в фоновом режиме, но я не хочу, чтобы конвейер выполнялся тысячами раз. Это фрагмент:

my $command = "program $data >> $log";
myExecute("$command");

myExecute в основном запускает команду, используя system(), наряду с некоторыми другими функциями ведения журнала/печати. Что я хочу сделать, так это:

my $command = "program $data & >> $log";

Это, очевидно, создаст большой конвейер. Есть ли способ ограничить количество фоновых исполнений одновременно (предпочтительно с помощью &)? (Я хотел бы попробовать 2-4).


person user3006471    schedule 18.11.2013    source источник


Ответы (2)


#!/bin/bash
#
# lets call this script "multi_script.sh"
#
#wait until there are less then 4 instances running
#polling with interval 5 seconds

while [ $( pgrep -c program ) -gt 4 ]; do sleep 5; done

/path/to/program "$1" &

Теперь назовите это так:

my $command = "multi_script.sh $data" >> $log;

Ваш perl-скрипт будет ждать, если ждет bash-скрипт.

Положительные моменты: в случае сбоя процесса он будет заменен (данные, разумеется, остаются необработанными)

Недостатки: важно, чтобы ваш Perl-скрипт выжидал некоторое время между запуском экземпляров (возможно, период ожидания в секунду) из-за задержки между вызовом скрипта и прохождением цикла while. Если вы создадите их слишком быстро (системный спам), вы получите гораздо больше процессов, чем рассчитывали.

person thom    schedule 19.11.2013
comment
Спасибо, Том. Это выглядит очень многообещающе. Простите меня, хотя я не могу полностью понять код, который вы предложили. Я просмотрел руководство pgrep и не вижу доступного флага -c. Вы имели в виду, что это -f? Я также не понимаю строку поиска, которую вы использовали: ${PROGRAM##*/}. Извините, если я немного медленно, но не могли бы вы объяснить мне это? - person user3006471; 20.11.2013
comment
Неважно, я понимаю, что вы сейчас пытаетесь подсчитать, поэтому я связался с 'wc -l'. Я до сих пор не понимаю часть ${PROGRAM##*/}, но в остальном я реализовал только 'pgrep PROGRAM | wc -l' и он работает как положено. Одна проблема, с которой я столкнусь, заключается в том, что я часто запускаю сценарий Perl в нескольких каталогах. Я не верю, что это позволит мне сделать это больше, так как он ищет любой процесс с именем программы в нем. - person user3006471; 20.11.2013
comment
@ user3006471, pgrep -c подсчитывает количество совпадающих экземпляров. Действительно, если ваш pgrep не имеет опции -c, то передайте ее в wc -l Не обращайте внимания на ${PROGRAM##*/}. Я сделал ошибку здесь. я уже исправил - person thom; 20.11.2013
comment
Возможен запуск скрипта в нескольких каталогах. Не могли бы вы рассказать немного о том, почему это может быть проблемой? - person thom; 20.11.2013
comment
Спасибо еще раз! В основном то, что я хочу сделать, это запустить мой perl-скрипт одновременно в нескольких каталогах. Например, если я хочу удалить каталог A в разделе 1 и каталог B в разделе 2, у меня обычно открываются 2 окна терминала, в которых запущен мой Perl-скрипт. Однако, поскольку pgrep проверяет только имя программы, которое вызывает сценарий perl, то 4 фоновых процесса, которые изначально предназначались для 1 экземпляра сценария perl, теперь будут разделены между двумя экземплярами и т. д. - person user3006471; 21.11.2013
comment
Да, один сценарий, вероятно, действительно уморит другого. Когда в вашем Perl-скрипте ваш вызов System() завершится, он убьет оболочку, и запущенные программы потеряют своих родителей и будут приняты init, поэтому будет невозможно отследить $PPID, кто был родителем. Если вы можете передать свои данные в System() вместо многократного вызова System(), это будет возможно. :-) - person thom; 21.11.2013

Если вы в состоянии изменить

my $command = "program $data & >> $log";

в

my $command = "cat $data >>/path/to/datafile";

(или даже лучше: добавьте $data в /path/to/datafile непосредственно из perl)
И когда ваш скрипт будет завершен, последней строкой будет:

System("/path/to/quadslotscript.sh");

то у меня есть скрипт quadslotscript.sh здесь:

  1. 4 слота исполнения запущены и остаются до конца
  2. все слоты получают ввод из одного и того же файла данных
  3. когда слот готов к обработке, он будет считывать новую запись для обработки
    до тех пор, пока файл данных/очередь не опустеет.
  4. нет обработки обрабатываемой таблицы во время выполнения, только когда вся работа выполнена.

код:

#!/bin/bash

#use the datafile as a queue where all processes get their input
exec 3< "/path/to/datafile"

#4 seperate processes 
while read -u 3 DATA; do "/path/to/program $DATA" >>$log; done &
while read -u 3 DATA; do "/path/to/program $DATA" >>$log; done &
while read -u 3 DATA; do "/path/to/program $DATA" >>$log; done &
while read -u 3 DATA; do "/path/to/program $DATA" >>$log; done &

#only exit when 100% sure that all processes ended
while pgrep "program" &>"/dev/null" ; do wait ; done
person thom    schedule 19.11.2013