web-dev-qa-db-ja.com

Linuxのコマンドキューからの並列処理(bash、python、Ruby ...など)

Linuxサーバーのシェルで実行する必要がある200個のコマンドのリスト/キューがあります。

一度に(キューから)最大10個のプロセスを実行したいだけです。一部のプロセスは完了するまでに数秒かかりますが、他のプロセスははるかに長くかかります。

プロセスが終了したら、次のコマンドをキューから「ポップ」して実行します。

誰かがこの問題を解決するためのコードを持っていますか?

詳細:

ある種のキューの中に、実行する必要のある200個の作業があります。一度に最大10個の作業を行いたいです。スレッドが作業を終了すると、キューに次の作業を要求する必要があります。キューにこれ以上作業がない場合、スレッドは終了するはずです。すべてのスレッドが終了すると、すべての作業が完了したことを意味します。

私が解決しようとしている実際の問題は、imapsyncを使用して、200個のメールボックスを古いメールサーバーから新しいメールサーバーに同期することです。メールボックスが大きくて同期に時間がかかるユーザーもいれば、メールボックスが非常に小さくてすばやく同期するユーザーもいます。

44
mlambie

Makeとmake-jxxコマンドを使用してこれを実行できると思います。

おそらくこのようなメイクファイル

all : usera userb userc....

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

make -j 10 -f makefile

40
ErgoSum

シェルでは、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 aliceprocess_name bob 等々。

Parallel この目的のために正確に作成されています。

cat userlist | parallel imapsync

他のソリューションと比較した Parallel の美しさの1つは、出力が混合されないようにすることです。 Paralleltracerouteを実行すると、次のように正常に機能します。

(echo foss.org.my; echo www.debian.org; echo www.freenetproject.org) | parallel traceroute
27
Ole Tange

この種のジョブのために、PPSSが記述されています:並列処理シェルスクリプト。この名前のグーグルとあなたはそれを見つけるでしょう、私はlinkspamしません。

13
Louwrentius

GNU make(およびおそらく他の実装も)には-j引数があり、これは一度に実行されるジョブの数を制御します。ジョブが完了すると、makeは別のジョブを開始します。

まあ、それらが互いにほとんど独立している場合、私は次の点で考えるでしょう:

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コマンドは、一部の子が終了するのではなく、すべての子が終了するのを待ちます。

4

Pythonを使用する場合は、 Twisted を使用することをお勧めします。

具体的には ツイストランナー

3
Bjorn

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自体はシングルスレッドです。ただし、IOを待機するという点では、複数のスレッドのメリットを享受できます。)

3
John Fouhy

https://savannah.gnu.org/projects/parallel (gnu parallel)とpsshが役立つかもしれません。

2
rogerdpack

Pythonの マルチプロセッシングモジュール はあなたの問題にうまく適合しているようです。これは、プロセスごとのスレッド化をサポートする高レベルのパッケージです。

1
Jeff Bauer

/ tmp内のロックファイルを使用して、4つ以下のサブシェルでジョブを並列化するzshの単純な関数。

重要な部分は、最初のテストの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
0
calandoa