Параллельная обработка из командной очереди в Linux (bash, python, ruby ... что угодно)
У меня есть список/очередь из 200 команд, которые мне нужно запустить в оболочке на сервере Linux.
Я хочу иметь только 10 запущенных процессов (из очереди). Для выполнения некоторых процессов потребуется несколько секунд, другие процессы потребуют гораздо больше времени.
Когда процесс завершится, я хочу, чтобы следующая команда была "выскочена" из очереди и выполнена.
Есть ли у кого-нибудь код для решения этой проблемы?
Дальнейшая разработка:
Там должно быть 200 работ, которые нужно выполнить, в какой-то очереди. Я хочу, чтобы не более 10 произведений продолжалось сразу. Когда нить заканчивает кусок работы, он должен задать очередь для следующей работы. Если в очереди больше нет работы, поток должен умереть. Когда все потоки скончались, это означает, что все работы были выполнены.
Фактическая проблема, которую я пытаюсь решить, заключается в использовании imapsync
для синхронизации 200 почтовых ящиков со старого почтового сервера на новый почтовый сервер. У некоторых пользователей большие почтовые ящики и длительная синхронизация tto, у других очень маленькие почтовые ящики и синхронизация.
Ответы
Ответ 1
Я бы предположил, что вы можете сделать это, используя make и команду make -j xx.
Возможно, файл makefile, подобный этому
all : usera userb userc....
usera:
imapsync usera
userb:
imapsync userb
....
make -j 10 -f makefile
Ответ 2
В оболочке xargs
можно использовать для обработки параллельной команды в очереди. Например, для того, чтобы всегда 3 спать параллельно, спать в течение 1 секунды каждый и выполнять 10 спящих в общей сложности
echo {1..10} | xargs -d ' ' -n1 -P3 sh -c 'sleep 1s' _
И он будет спать всего 4 секунды. Если у вас есть список имен и вы хотите передать имена выполняемым командам, снова выполняя 3 команды параллельно, сделайте
cat names | xargs -n1 -P3 process_name
Выполняет команду process_name alice
, process_name bob
и т.д.
Ответ 3
Parallel сделано для этого специально.
cat userlist | parallel imapsync
Одна из красот Parallel по сравнению с другими решениями заключается в том, что она гарантирует, что результат не смешан. Выполнение traceroute
в Parallel работает отлично, например:
(echo foss.org.my; echo www.debian.org; echo www.freenetproject.org) | parallel traceroute
Ответ 4
Для этого вида работы PPSS написано: Параллельная обработка оболочки script. Google для этого имени, и вы его найдете, я не буду ссылаться.
Ответ 5
GNU make (и, возможно, другие реализации) имеет аргумент -j, который определяет, сколько заданий он будет запускать сразу. Когда задание завершится, make запустит еще один.
Ответ 6
Хорошо, если они в значительной степени независимы друг от друга, я бы подумал с точки зрения:
Initialize an array of jobs pending (queue, ...) - 200 entries
Initialize an array of jobs running - empty
while (jobs still pending and queue of jobs running still has space)
take a job off the pending queue
launch it in background
if (queue of jobs running is full)
wait for a job to finish
remove from jobs running queue
while (queue of jobs is not empty)
wait for job to finish
remove from jobs running queue
Обратите внимание, что тест хвоста в основном цикле означает, что если "очередь выполнения заданий" имеет место, когда цикл while повторяется, это предотвращает преждевременное завершение цикла. Я думаю, что логика звучит.
Я вижу, как сделать это на C довольно легко - это не будет так сложно в Perl, и (и поэтому не слишком сложно на других языках сценариев - Python, Ruby, Tcl и т.д.). Я совсем не уверен, что хочу сделать это в оболочке - команда wait
в оболочке ожидает, что все дети завершатся, а не для того, чтобы какой-либо дочерний объект завершился.
Ответ 7
В python вы можете попробовать:
import Queue, os, threading
# synchronised queue
queue = Queue.Queue(0) # 0 means no maximum size
# do stuff to initialise queue with strings
# representing os commands
queue.put('sleep 10')
queue.put('echo Sleeping..')
# etc
# or use python to generate commands, e.g.
# for username in ['joe', 'bob', 'fred']:
# queue.put('imapsync %s' % username)
def go():
while True:
try:
# False here means no blocking: raise exception if queue empty
command = queue.get(False)
# Run command. python also has subprocess module which is more
# featureful but I am not very familiar with it.
# os.system is easy :-)
os.system(command)
except Queue.Empty:
return
for i in range(10): # change this to run more/fewer threads
threading.Thread(target=go).start()
Непроверено...
(конечно, сам python является однопоточным. Однако вы все равно получите преимущество нескольких потоков в ожидании ввода-вывода.)
Ответ 8
Если вы собираетесь использовать Python, я рекомендую использовать Twisted.
В частности Twisted Runner.
Ответ 9
https://savannah.gnu.org/projects/parallel (gnu parallel)
и pssh может помочь.
Ответ 10
Python модуль многопроцессорности, похоже, будет хорошо соответствовать вашей проблеме. Это пакет высокого уровня, который поддерживает потоки по процессу.
Ответ 11
Простая функция в zsh для параллелизации заданий не более чем с 4 подоболочками с использованием файлов блокировки в /tmp.
Единственной нетривиальной частью являются флаги glob в первом тесте:
-
#q
: включить подшивку имени файла в тесте
-
[4]
: возвращает только 4-й результат
-
N
: игнорировать ошибку при пустом результате
Это должно быть легко преобразовать в posix, хотя было бы немного более подробным.
Не забывайте избегать кавычек в заданиях с помощью \"
.
#!/bin/zsh
setopt extendedglob
para() {
lock=/tmp/para_$$_$((paracnt++))
# sleep as long as the 4th lock file exists
until [[ -z /tmp/para_$$_*(#q[4]N) ]] { sleep 0.1 }
# Launch the job in a subshell
( touch $lock ; eval $* ; rm $lock ) &
# Wait for subshell start and lock creation
until [[ -f $lock ]] { sleep 0.001 }
}
para "print A0; sleep 1; print Z0"
para "print A1; sleep 2; print Z1"
para "print A2; sleep 3; print Z2"
para "print A3; sleep 4; print Z3"
para "print A4; sleep 3; print Z4"
para "print A5; sleep 2; print Z5"
# wait for all subshells to terminate
wait
Ответ 12
Вы можете уточнить, что вы подразумеваете под параллелью? Похоже, вам нужно реализовать некоторую блокировку в очереди, чтобы ваши записи не были выбраны дважды, и т.д., А команды запускаются только один раз.
Большинство систем очередей обманывают - они просто пишут гигантский список дел, затем выбирают, например. десять предметов, работать с ними и выбирать следующие десять предметов. Там нет распараллеливания.
Если вы предоставите более подробную информацию, я уверен, что мы можем вам помочь.