Linuxサーバーのシェルで実行する必要がある200個のコマンドのリスト/キューがあります。
一度に(キューから)最大10個のプロセスを実行したいだけです。一部のプロセスは完了するまでに数秒かかりますが、他のプロセスははるかに長くかかります。
プロセスが終了したら、次のコマンドをキューから「ポップ」して実行します。
誰かがこの問題を解決するためのコードを持っていますか?
詳細:
ある種のキューの中に、実行する必要のある200個の作業があります。一度に最大10個の作業を行いたいです。スレッドが作業を終了すると、キューに次の作業を要求する必要があります。キューにこれ以上作業がない場合、スレッドは終了するはずです。すべてのスレッドが終了すると、すべての作業が完了したことを意味します。
私が解決しようとしている実際の問題は、imapsync
を使用して、200個のメールボックスを古いメールサーバーから新しいメールサーバーに同期することです。メールボックスが大きくて同期に時間がかかるユーザーもいれば、メールボックスが非常に小さくてすばやく同期するユーザーもいます。
Makeとmake-jxxコマンドを使用してこれを実行できると思います。
おそらくこのようなメイクファイル
all : usera userb userc....
usera:
imapsync usera
userb:
imapsync userb
....
make -j 10 -f makefile
シェルでは、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
等々。
この種のジョブのために、PPSSが記述されています:並列処理シェルスクリプト。この名前のグーグルとあなたはそれを見つけるでしょう、私はlinkspamしません。
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
コマンドは、一部の子が終了するのではなく、すべての子が終了するのを待ちます。
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を待機するという点では、複数のスレッドのメリットを享受できます。)
https://savannah.gnu.org/projects/parallel (gnu parallel)とpsshが役立つかもしれません。
Pythonの マルチプロセッシングモジュール はあなたの問題にうまく適合しているようです。これは、プロセスごとのスレッド化をサポートする高レベルのパッケージです。
/ 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