2013-06-25 12 views
20

Było kilka podobnych pytań, ale moim problemem nie jest "uruchamianie kilku programów równolegle" - co można w prosty sposób wykonać przy pomocy parallel lub xargs.Jak uruchomić daną funkcję w Bash równolegle?

Potrzebuję zrównoleglić funkcje Bash.

Wyobraźmy sobie kod tak:

for i in "${list[@]}" 
do 
    for j in "${other[@]}" 
    do 
    # some processing in here - 20-30 lines of almost pure bash 
    done 
done 

Niektóre przetwarzania wymaga połączenia do zewnętrznych programów.

Chciałbym uruchomić niektóre (4-10) zadań, każdy działa dla różnych $i. Łączna liczba elementów na liście $ wynosi> 500.

Wiem, że mogę umieścić całą pętlę for j ... done w zewnętrznym skrypcie i po prostu wywołać ten program równolegle, ale czy można to zrobić bez podziału funkcji między dwa oddzielne programy ?

+0

Spróbuj użyć funkcji. Lub coś w stylu 'var1 = \' ls && pwd && ls && pwd & \ '' wewnątrz twojej pętli. –

+0

Tak, ale nie chcę uruchamiać * wszystkich * iteracji na raz. Chciałbym mieć 4 równoczesne procesy robocze, a jeśli któryś z nich się zakończy - zacznij nowy. Rodzaj: 'cat work_params | xargs -L1 -P4 do_bit_of_work' działa. –

+0

Hmmm ... 4 pracowników. Sądzę, że będziesz musiał wtedy ustawić jakieś semafory. I przepytywać ich okresowo. –

Odpowiedz

10

Edytuj: Proszę rozważyć Ole's answer zamiast tego.

Zamiast osobnego skryptu, możesz umieścić swój kod w oddzielnej funkcji bash. można następnie wyeksportować go i uruchom go poprzez xargs:

#!/bin/bash 
dowork() { 
    sleep $((RANDOM % 10 + 1)) 
    echo "Processing i=$1, j=$2" 
} 
export -f dowork 

for i in "${list[@]}" 
do 
    for j in "${other[@]}" 
    do 
     printf "%s\0%s\0" "$i" "$j" 
    done 
done | xargs -0 -n 2 -P 4 bash -c 'dowork "[email protected]"' -- 
+0

To może zadziałać, dzięki. –

32

sem jest częścią GNU Parallel i jest dla tego rodzaju sytuacji.

for i in "${list[@]}" 
do 
    for j in "${other[@]}" 
    do 
     # some processing in here - 20-30 lines of almost pure bash 
     sem -j 4 dolong task 
    done 
done 

Jeśli lubisz funkcja lepiej GNU Równolegle można zrobić podwójnej pętli w jednym zamachem:

dowork() { 
    echo "Starting i=$1, j=$2" 
    sleep 5 
    echo "Done i=$1, j=$2" 
} 
export -f dowork 

parallel dowork ::: "${list[@]}" ::: "${other[@]}" 
+3

Awesome. Droga gładsza niż moja sugestia. –

+2

To nie działa (już?) (GNU równolegle 20160722)., Wyjścia "/ bin/bash: dowork: command not found". – jamshid

+1

@jamshid Czy pamiętasz 'eksport -f dowork'? Jeśli tak, być może znalazłeś błąd, który pojawia się w twoim systemie. Jako obejście: wypróbuj 'env_parallel' zamiast' parallel'. –

0

rozwiązanie, aby uruchomić multi-wiersz poleceń równolegle:

for i in "${list[@]}" 
do 
    for j in "${other[@]}" 
    do 
     test "$(jobs | wc -l)" -ge 8 && wait -n || true 
     (
      your 
      multi-line 
      commands 
      here 
     ) & 
    done 
done 

Jeśli istnieje już 8 zadań bash, wait będzie czekać na ukończenie co najmniej jednego zadania. Jeśli/kiedy jest mniej zadań, uruchamia nowe asynchronicznie.

Korzyści z takiego podejścia:

  1. To bardzo proste do poleceń multi-line. Wszystkie zmienne są automatycznie "przechwytywane" w zakresie, nie trzeba przekazywać ich jako argumentów.
  2. Jest względnie szybki. Porównaj to, na przykład, z równoległym (podaję oficjalny man):

    równolegle jest wolny przy uruchamianiu - około 250 ms za pierwszym razem i 150 ms po tym.

  3. Tylko do pracy potrzebuje bash.

Wady:

  1. Istnieje możliwość, że było 8 miejsc pracy, kiedy je policzyć, ale mniej, kiedy zaczęliśmy czekać. (Dzieje się tak, jeśli zadania kończą się w ciągu milisekund między tymi dwoma poleceniami). Dzięki temu możemy wykonać mniej zadań niż jest to wymagane.Jednak zostanie wznowione po zakończeniu co najmniej jednego zadania lub natychmiast po uruchomieniu 0 zadań (w tym przypadku natychmiast wychodzi z tego wait -n).
  2. Bardzo mało prawdopodobne, ale jeśli użyjesz kontroli basha (&) do innych celów w tej samej pętli, rzeczy mogą działać niezgodnie z oczekiwaniami.
Powiązane problemy