2009-09-21 9 views
41

Mi piacerebbe rendere una parte del mio codice più efficiente. Sto pensando di renderlo forkged in più processi e farli eseguire 50/100 volte in una volta, invece di una sola volta.Processi di biforcazione/filettatura multipla | Bash

Ad esempio (pseudo):

for line in file; 
do 
foo; 
foo2; 
foo3; 
done 

desidero questo ciclo for per eseguire più volte. So che questo può essere fatto con il biforcarsi. Sarebbe qualcosa di simile?

while(x <= 50) 
parent(child pid) 
{ 
    fork child() 
} 
child 
{ 
    do 
    foo; foo2; foo3; 
    done 
    return child_pid() 
} 

Oppure sto pensando a questo nel modo sbagliato?

Grazie!

risposta

2

Vorrei cercare esempio

for x in 1 2 3 ; do { echo a $x ; sleep 1 ; echo b $x ; } & done ; sleep 10 

e utilizzare jobs per vedere cosa è in esecuzione.

26

Non conosco nessuna chiamata esplicita fork in bash. Quello che probabilmente si vorrebbe fare è aggiungere & a un comando che si desidera eseguire in background. È inoltre possibile utilizzare & sulle funzioni che si definiscono all'interno di uno script bash:

do_something_with_line() 
{ 
    line=$1 
    foo 
    foo2 
    foo3 
} 

for line in file 
do 
    do_something_with_line $line & 
done 

EDIT: di porre un limite al numero di simultanee processi in background, si potrebbe provare qualcosa di simile:

for line in file 
do 
    while [`jobs | wc -l` -ge 50 ] 
    do 
    sleep 5 
    done 
    do_something_with_line $line & 
done 
+1

Hai miscapitalized il fa_qualcosa ... nome ;-) –

+1

Got it - che dire quando voglio per assicurarsi che io solo sono eseguire 50 istanze alla volta? E - quando uno di questi processi è terminato, assicurati che venga generato ancora 1 altro. – Greg

+0

Usa il comando di bash 'jobs'. –

46

In script bash (non interattivi) per impostazione predefinita JOB CONTROL è disabilitato in modo da non poter eseguire i comandi: job, fg e bg.

Ecco ciò che funziona bene per me:

#!/bin/sh 

set -m # Enable Job Control 

for i in `seq 30`; do # start 30 jobs in parallel 
    sleep 3 & 
done 

# Wait for all parallel jobs to finish 
while [ 1 ]; do fg 2> /dev/null; [ $? == 1 ] && break; done 

L'ultima riga utilizza "fg" per portare un processo in background in primo piano. Lo fa in un ciclo fino a quando fg restituisce 1 ($? == 1), che fa quando non ci sono più lavori in background.

+16

In script bash, è possibile usare 'wait', ad esempio:' sleep 3 & WAITPID = $ !; aspetta $ WAITPID', o concentra i pid in questo modo 'WAITPIDS =" $ WAITPIDS "$!; ...; aspetta $ WAITPIDS' –

+9

O semplicemente "aspetta". – lethalman

+0

come farei 1000 cose, 50 alla volta?in un ciclo di dire '$ (seq 1 1000)' – chovy

17

con GNU parallelo si può fare:

cat file | parallel 'foo {}; foo2 {}; foo3 {}' 

Questo verrà eseguito un lavoro su ogni core della CPU. Per eseguire 50 fare:

cat file | parallel -j 50 'foo {}; foo2 {}; foo3 {}' 

Guarda i video introduttivi per saperne di più:

http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

+2

Ad eccezione del nag-screen, il parallelo è piuttosto interessante. –

+1

Vorrei aggiungere che il parallelo è già installato sulla maggior parte dei sistemi. La mia macchina OS X 10.8.5 ce l'ha. È tempo per me di rispolverare le ragnatele dal mio script di shell e aggiornare i miei cicli per parallelo ... – labyrinth

+0

questo sembra diventare molto caotico quando si utilizza la ricerca/sostituzione che ha caratteri che devono essere sfuggiti. –

2

In base a quello che tutti voi condiviso sono stato in grado di mettere questo insieme:

#!/usr/bin/env bash 

VAR1="192.168.1.20 192.168.1.126 192.168.1.36" 

for a in $VAR1; do { ssh -t -t $a -l Administrator "sudo softwareupdate -l"; } & done; 
WAITPIDS="$WAITPIDS "$!;...; wait $WAITPIDS 
echo "Script has finished" 

Exit 1 

Questo elenca tutti gli aggiornamenti sul Mac su tre macchine contemporaneamente. Più tardi l'ho usato per eseguire un aggiornamento software per tutte le macchine quando eseguo CAT il mio indirizzo IP.txt

0

Ecco la mia funzione di controllo del filo:

#!/bin/bash 
# This function just checks jobs in background, don't do more things. 
# if jobs number is lower than MAX, then return to get more jobs; 
# if jobs number is greater or equal to MAX, then wait, until someone finished. 

# Usage: 
# thread_max 8 
# thread_max 0 # wait, until all jobs completed 

thread_max() { 
    local CHECK_INTERVAL="3s" 
    local CUR_THREADS= 
    local MAX= 
    [[ $1 ]] && MAX=$1 || return 127 

    # reset MAX value, 0 is easy to remember 
    [ $MAX -eq 0 ] && { 
     MAX=1 
     DEBUG "waiting for all tasks finish" 
    } 

    while true; do 
     CUR_THREADS=`jobs -p | wc -w` 

     # workaround about jobs bug. If don't execute it explicitily, 
     # CUR_THREADS will stick at 1, even no jobs running anymore. 
     jobs &>/dev/null 

     DEBUG "current thread amount: $CUR_THREADS" 
     if [ $CUR_THREADS -ge $MAX ]; then 
      sleep $CHECK_INTERVAL 
     else 
      return 0 
     fi 
    done 
} 
12

non mi piace usare wait perché viene bloccato fino a quando le uscite di processo, che non è l'ideale quando ci sono processi più ad aspettare su come posso' t ottenere un aggiornamento di stato fino al termine del processo corrente. Preferisco utilizzare una combinazione di kill -0 e sleep a questo.

Dato che un array di pids è in attesa, utilizzo la funzione waitPids() sottostante per ottenere un feedback continuo su quali pid sono ancora in attesa di completamento.

declare -a pids 
waitPids() { 
    while [ ${#pids[@]} -ne 0 ]; do 
     echo "Waiting for pids: ${pids[@]}" 
     local range=$(eval echo {0..$((${#pids[@]}-1))}) 
     local i 
     for i in $range; do 
      if ! kill -0 ${pids[$i]} 2> /dev/null; then 
       echo "Done -- ${pids[$i]}" 
       unset pids[$i] 
      fi 
     done 
     pids=("${pids[@]}") # Expunge nulls created by unset. 
     sleep 1 
    done 
    echo "Done!" 
} 

Quando inizio a un processo in background, aggiungo il suo pid subito alla matrice pids utilizzando questo sotto funzione di utilità:

addPid() { 
    desc=$1 
    pid=$2 
    echo "$desc -- $pid" 
    pids=(${pids[@]} $pid) 
} 

Ecco un esempio che mostra come utilizzare:

for i in {2..5}; do 
    sleep $i & 
    addPid "Sleep for $i" $! 
done 
waitPids 

Ed ecco come si presenta il feedback:

Sleep for 2 -- 36271 
Sleep for 3 -- 36272 
Sleep for 4 -- 36273 
Sleep for 5 -- 36274 
Waiting for pids: 36271 36272 36273 36274 
Waiting for pids: 36271 36272 36273 36274 
Waiting for pids: 36271 36272 36273 36274 
Done -- 36271 
Waiting for pids: 36272 36273 36274 
Done -- 36272 
Waiting for pids: 36273 36274 
Done -- 36273 
Waiting for pids: 36274 
Done -- 36274 
Done! 
0

L'approccio di haridsv è ottimo, offre la flessibilità necessaria per eseguire un'installazione di slot del processore in cui è possibile mantenere un numero di processi in esecuzione con nuovi lavori che vengono inviati come lavori completi, mantenendo il carico generale elevato. Ecco le mie modifiche al codice di haridsv per un processore n-slot per una 'griglia' di ngrid 'lavori' (lo uso per griglie di modelli di simulazione) Seguito da output di test per 8 lavori 3 alla volta, con totali parziali di esecuzione , ha presentato, a termine e rimanendo uscita

#!/bin/bash 
######################################################################## 
# see haridsv on forking-multi-threaded-processes-bash 
# loop over grid, submitting jobs in the background. 
# As jobs complete new ones are set going to keep the number running 
# up to n as much as possible, until it tapers off at the end. 
# 
# 8 jobs 
ngrid=8 
# 3 at a time 
n=3 
# running counts 
running=0 
completed=0 
# previous values 
prunning=0 
pcompleted=0 
# 
######################################################################## 
# process monitoring functions 
# 
declare -a pids 
# 
function checkPids() { 
echo ${#pids[@]} 
if [ ${#pids[@]} -ne 0 ] 
then 
    echo "Checking for pids: ${pids[@]}" 
    local range=$(eval echo {0..$((${#pids[@]}-1))}) 
    local i 
    for i in $range; do 
     if ! kill -0 ${pids[$i]} 2> /dev/null; then 
      echo "Done -- ${pids[$i]}" 
      unset pids[$i] 
      completed=$(expr $completed + 1) 
     fi 
    done 
    pids=("${pids[@]}") # Expunge nulls created by unset. 
    running=$((${#pids[@]})) 
    echo "#PIDS :"$running 
fi 
} 
# 
function addPid() { 
    desc=$1 
    pid=$2 
    echo " ${desc} - "$pid 
    pids=(${pids[@]} $pid) 
} 
######################################################################## 
# 
# Loop and report when job changes happen, 
# keep going until all are completed. 
# 
idx=0 
while [ $completed -lt ${ngrid} ] 
do 
# 
    if [ $running -lt $n ] && [ $idx -lt ${ngrid} ] 
    then 
#################################################################### 
# 
# submit a new process if less than n 
# are running and we haven't finished... 
# 
# get desc for process 
# 
     name="job_"${idx} 
# background execution 
     sleep 3 & 
     addPid $name $! 
     idx=$(expr $idx + 1) 
# 
#################################################################### 
# 
    fi 
# 
    checkPids 
# if something changes... 
    if [ ${running} -gt ${prunning} ] || \ 
     [ ${completed} -gt ${pcompleted} ] 
    then 
     remain=$(expr $ngrid - $completed) 
     echo " Running: "${running}" Submitted: "${idx}\ 
       " Completed: "$completed" Remaining: "$remain 
    fi 
# save counts to prev values 
    prunning=${running} 
    pcompleted=${completed} 
# 
    sleep 1 
# 
done 
# 
######################################################################## 

prova:

job_0 - 75257 
1 
Checking for pids: 75257 
#PIDS :1 
Running: 1 Submitted: 1 Completed: 0 Remaining: 8 
job_1 - 75262 
2 
Checking for pids: 75257 75262 
#PIDS :2 
Running: 2 Submitted: 2 Completed: 0 Remaining: 8 
job_2 - 75267 
3 
Checking for pids: 75257 75262 75267 
#PIDS :3 
Running: 3 Submitted: 3 Completed: 0 Remaining: 8 
3 
Checking for pids: 75257 75262 75267 
Done -- 75257 
#PIDS :2 
Running: 2 Submitted: 3 Completed: 1 Remaining: 7 
job_3 - 75277 
3 
Checking for pids: 75262 75267 75277 
Done -- 75262 
#PIDS :2 
Running: 2 Submitted: 4 Completed: 2 Remaining: 6 
job_4 - 75283 
3 
Checking for pids: 75267 75277 75283 
Done -- 75267 
#PIDS :2 
Running: 2 Submitted: 5 Completed: 3 Remaining: 5 
job_5 - 75289 
3 
Checking for pids: 75277 75283 75289 
#PIDS :3 
Running: 3 Submitted: 6 Completed: 3 Remaining: 5 
3 
Checking for pids: 75277 75283 75289 
Done -- 75277 
#PIDS :2 
Running: 2 Submitted: 6 Completed: 4 Remaining: 4 
job_6 - 75298 
3 
Checking for pids: 75283 75289 75298 
Done -- 75283 
#PIDS :2 
Running: 2 Submitted: 7 Completed: 5 Remaining: 3 
job_7 - 75304 
3 
Checking for pids: 75289 75298 75304 
Done -- 75289 
#PIDS :2 
Running: 2 Submitted: 8 Completed: 6 Remaining: 2 
2 
Checking for pids: 75298 75304 
#PIDS :2 
2 
Checking for pids: 75298 75304 
Done -- 75298 
#PIDS :1 
Running: 1 Submitted: 8 Completed: 7 Remaining: 1 
1 
Checking for pids: 75304 
Done -- 75304 
#PIDS :0 
Running: 0 Submitted: 8 Completed: 8 Remaining: 0 
Problemi correlati