Параллельная обработка из очереди команд в Linux (bash, python, ruby ​​... что угодно) - PullRequest
42 голосов
/ 21 января 2009

У меня есть список / очередь из 200 команд, которые мне нужно запустить в оболочке на сервере Linux.

Я хочу, чтобы одновременно выполнялось не более 10 процессов (из очереди). Некоторые процессы завершатся за несколько секунд, другие - намного дольше.

Когда процесс завершится, я хочу, чтобы следующая команда «выскочила» из очереди и была выполнена.

У кого-нибудь есть код для решения этой проблемы?

Дальнейшая разработка:

Есть 200 работ, которые нужно выполнить, в какой-то очереди. Я хочу, чтобы одновременно работало не более 10 штук. Когда поток завершает часть работы, он должен запросить очередь для следующей части работы. Если в очереди больше нет работы, поток должен умереть. Когда все потоки исчезли, это означает, что вся работа была выполнена.

Фактическая проблема, которую я пытаюсь решить, - это использование imapsync для синхронизации 200 почтовых ящиков со старого почтового сервера на новый почтовый сервер. Некоторые пользователи имеют большие почтовые ящики и занимают много времени для синхронизации, другие имеют очень маленькие почтовые ящики и быстро синхронизируются.

Ответы [ 12 ]

44 голосов
/ 21 января 2009

В оболочке 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 и т. Д.

38 голосов
/ 21 января 2009

Я думаю, вы могли бы сделать это, используя make и команду make -j xx.

Возможно, такой файл сборки

all : usera userb userc....

usera:
       imapsync usera
userb:
       imapsync userb
....

make -j 10 -f makefile

25 голосов
/ 27 января 2010

Параллельно сделано именно для этой цели.

cat userlist | parallel imapsync

Одна из красот Параллельная по сравнению с другими решениями заключается в том, что она гарантирует, что выход не смешан. Выполнение traceroute in Parallel работает нормально, например:

(echo foss.org.my; echo www.debian.org; echo www.freenetproject.org) | parallel traceroute
13 голосов
/ 10 марта 2009

Для такого рода заданий в PPSS написано: Параллельная обработка сценария оболочки. Google для этого имени, и вы найдете его, я не буду ссылаться на спам.

7 голосов
/ 21 января 2009

GNU make (и, возможно, другие реализации) имеют аргумент -j, который определяет, сколько заданий он будет выполнять одновременно Когда работа завершится, make запустит другую.

4 голосов
/ 21 января 2009

Ну, если бы они были в значительной степени независимы друг от друга, я бы подумал с точки зрения:

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 в оболочке ожидает завершения всех дочерних элементов, а не завершения какого-либо дочернего элемента.

3 голосов
/ 21 января 2009

В 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 является однопоточным. Тем не менее, вы все равно должны использовать преимущества нескольких потоков в плане ожидания ввода-вывода.)

3 голосов
/ 21 января 2009

Если вы собираетесь использовать Python, я рекомендую использовать для этого Twisted

В частности Витой бегун .

2 голосов
/ 30 августа 2011

https://savannah.gnu.org/projects/parallel (GNU параллельно) и pssh может помочь.

1 голос
/ 21 января 2009

Многопроцессорный модуль Python , казалось бы, хорошо подошел к вашей проблеме. Это высокоуровневый пакет, поддерживающий многопоточность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...