Подтвердить что ты не робот

Как запустить заданную функцию в Bash параллельно?

Были некоторые аналогичные вопросы, но моя проблема заключается не в том, чтобы "запускать несколько программ параллельно" - это можно сделать тривиально с помощью parallel или xargs.

Мне нужно распараллелить функции Bash.

Представьте себе такой код:

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

Некоторая обработка требует вызовов внешних программ.

Я хотел бы запустить некоторые (4-10) задачи, каждый из которых работает для разных $i. Общее количество элементов в $list составляет > 500.

Я знаю, что могу поместить весь цикл for j ... done во внешний script и просто вызвать эту программу параллельно, но можно ли обойтись без разделения функций между двумя отдельными программами?

4b9b3361

Ответ 1

Изменить: Пожалуйста, рассмотрите Ole answer.

Вместо отдельного script вы можете поместить свой код в отдельную функцию bash. Затем вы можете экспортировать его и запустить через 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]"' -- 

Ответ 2

sem является частью GNU Parallel и используется для такого рода ситуаций.

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

Если вам нравится функция лучше, GNU Parallel может сделать двойной цикл for за один раз:

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

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

Ответ 3

Решение для параллельной работы нескольких строк:

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

Если уже запущено 8 bash заданий, wait будет ждать завершения хотя бы одного задания. Если/при меньшем количестве заданий, он запускает новые асинхронно.

Преимущества такого подхода:

  • Это очень легко для многострочных команд. Все ваши переменные автоматически "захватываются" в области видимости, не нужно передавать их в качестве аргументов
  • Это относительно быстро. Сравните это, например, с параллельным (я цитирую официальный man):
    Параллельная параллель медленна при запуске - около 250 мс в первый раз и 150 мс после этого.
  • Для работы требуется bash.

Downsides:

  • Есть вероятность, что было 8 заданий, когда мы их подсчитали, но меньше, когда мы начали ждать. (Это происходит, если задание заканчивается в эти миллисекунды между двумя командами.) Это может сделать нас wait с меньшим количеством заданий, чем требуется. Тем не менее, он возобновится, когда по крайней мере одно задание завершится или сразу же, если есть 0 заданий (wait -n немедленно выйдет в этом случае).
  • Очень маловероятно, но если вы используете bash управление заданиями (&) для других целей в пределах одного цикла, все может работать не так, как ожидалось.