2016-03-11 2 views
1

Я выполняю несколько тестов параллельно, вызывая процесс из сценария. Каждый процесс выводит только на stdout> файл и завершает 0, если успешно (в противном случае -1).ограничение порожденных параллельных процессов и выход из всех после отказа любого

Если процесс завершает работу с -1, я печатаю что-то в его (или связанном) выходном файле (а именно с аргументами, с которыми он был вызван), убивает все остальные процессы и завершает работу.

Я написал сценарий, используя trap "..." CHLD, чтобы запустить некоторый код при выходе из подпроцесса, и это работает при определенных условиях, но я считаю, что мой скрипт не очень надежный. Если я посылаю прерывание клавиатуры, иногда подпроцессы продолжаются, и иногда количество подпроцессов просто перегружает машину (-ы), и ни одна из них, похоже, не продвигается вперед.

Я использую это на своем четырехъядерном ноутбуке, а также кластер из 128 процессоров, по которым подпроцессы распределяются автоматически. Как запустить большое количество подпроцессов фона в сценарии bash, ограниченное некоторым количеством из них, выполняемым одновременно, и сделать что-то + выход, если один из них возвращается с неправильным кодом? Мне также нужен сценарий для очистки после прерывания клавиатуры. Должен ли я использовать GNU-параллель? как?

Вот MWE моего сценария до сих пор, который беспрепятственно создает подпроцессы, аннотированные тем, что, как я думаю, означает каждая часть. Я пришла в голову идея использовать trap из shell - get exit code of background process

$ cat parallel_tests.sh 
#!/bin/bash 
# some help from https://stackoverflow.com/questions/1570262/shell-get-exit-code-of-background-process 
handle_chld() { 
     #echo pids are ${pids[@]} 
    local tmp=() ###temporary storage for pids that haven't finished 
     #for each pid that hadn't finished since the last trap 
    for((i=0;i<${#pids[@]};++i)); do 
       #if this pid is still running 
     if [[ $(ps -p ${pids[i]} -o pid=) ]] 
       then 
         tmp+=(${pids[i]}) ### add pid to list of pids that are running 
       else 
      wait ${pids[i]} ### put the exit code of this pid into $? 
         if [ "$?" != "0" ] ### if the exit code $? is non-zero 
         then 
           #kill all remaning processes 
           for((j=0;j<${#pids[@]};++j)) 
           do 
             if [[ $(ps -p ${pids[j]} -o pid=) ]] 
             then 
              echo killing child processes of ${pids[j]} 
              pkill -P ${pids[j]} 
             fi 
           done 
           cat _tmp${pids[i]} 
           #print things to the terminal here 
           echo "FAILED process ${pids[i]} args: `cat _tmpargs${pids[i]}`" 
           exit 1 
         else 
           echo "FINISHED process ${pids[i]} args: `cat _tmpargs${pids[i]}`" 
         fi 
     fi 
    done 
     #update list of running pids 
    pids=(${tmp[@]}) 
} 
# set this to monitor SIGCHLD 
set -o monitor 
# call handle_chld() when SIGCHLD signal is triggered 
trap "handle_chld" CHLD 

ALL_ARGS="2 32 87" ### ad nauseam 
for A in $ALL_ARGS; do 
     (sleep $A; false) > _tmp$! & 
     pids+=($!) 
     echo $A > _tmpargs${pids[${#pids[@]}-1]} 
     echo "STARTED process ${pids[${#pids[@]}-1]} args: `cat _tmpargs${pids[${#pids[@]}-1]}`" 
done 
echo "Every process started. Now waiting on PIDS:" 
echo ${pids[@]} 
wait ${pids[@]} ###wait until every process is finished (or exit in the trap) 

Выход этой версии после того, как 2 + эпсилон секунд:

$ ./parallel_tests.sh 
STARTED process 66369 args: 2 
STARTED process 66374 args: 32 
STARTED process 66381 args: 87 
Every process started. Now waiting on PIDS: 
66369 66374 66381 
killing child processes of 66374 
./parallel_tests.sh: line 43: 66376 Terminated: 15   sleep $A 
killing child processes of 66381 
./parallel_tests.sh: line 43: 66383 Terminated: 15   sleep $A 
FAILED process 66369 args: 2 

По существу, Pid 66369 терпит неудачу первый, а остальные два процесса рассматриваются в ловушке. Я упростил здесь процесс тестирования, поэтому мы не можем предположить, что я буду вручную вставлять wait с до появления новых. Кроме того, некоторые из тестовых процессов могут быть почти мгновенными. По сути, у меня есть целый беспорядок тестовых процессов, длинных и коротких, начиная, как только ресурсы могут быть выделены.

Я не уверен, что вызывает проблемы, о которых я упоминал выше, так как этот сценарий использует несколько новых функций для меня. Общие указатели приветствуются!

(я видел this question и он не отвечает на мой вопрос)

+0

Убивание задания, выполняющегося на удаленном кластере при нажатии CTRL-C, нетривиально, поэтому, даже если вы найдете решение, которое работает локально, предположим, что он просто работает удаленно. –

ответ

1
cat arguments | parallel --halt now,fail=1 my_prg 

В качестве альтернативы:

parallel --halt now,fail=1 my_prg ::: $ALL_ARGS 

GNU Parallel разработан таким образом, он убивает также удаленные рабочие места. Он делает это, используя группы процессов и тяжелые сценарии perl на удаленном сервере: https://www.gnu.org/software/parallel/parallel_design.html#The-remote-system-wrapper

+0

Если я могу уточнить для себя: я пишу каждый элемент $ ALL_ARGS в файл 'arguments' (по одному в строке), а затем cat/pipe это на параллель, где' my_prg' - это процесс, который принимает аргументы (например, ' спать "в моем примере). Могу ли я получить результат выполнения заданий и распечатать аргументы неудавшегося задания? – Alejandro

+0

Да, вы можете это получить. –

 Смежные вопросы

  • Нет связанных вопросов^_^