Bashスクリプトには次のプログラムがあります
for i in {1..1000}
do
foo i
done
パラメータを使用して関数をfoo
1000回呼び出します。i
複数のプロセスで実行されますが、同時に実行されないようにするにはどうすればよいですか?
だから私が持っているなら
for i in {1..1000}
do
foo i &
done
1000のプロセスがすべて同時に開始されますが、これは私が望むものではありません。
常に100個のプロセスを実行する方法はありますか?一部のプロセスが完了すると、1000回の繰り返しがすべて完了するまで、いくつかの新しいプロセスが開始されます。または、100個すべてが完了するのを待ってから100個を実行することもできます。
答え1
zsh
代わりに使用してくださいbash
:
autoload -Uz zargs
zargs -P100 -I{} -- {1..1000} -- foo {}
しかし、GNUがある場合は、xargs
次のようにすることもできます(zsh
、ksh93
またはからbash
):
xargs -I{} -P100 -a <(echo {1..1000}) foo {}
foo
しかしそれは独立した命令でなければならない。シェル関数や組み込み関数では使用できません。
zsh
'はzargs
順番に実行されます。 100ジョブを開始し、すべてのジョブが返されるのを待ってから、100ジョブの次の配置を開始します。一方、GNUはxargs
最大100個のジョブを実行し続けようとします。 100 件のジョブを開始し、別のジョブを開始して一度に 1 つずつ完了します。
この動作を実現するには、zshからバックグラウンドプロセスが返されるたびにトリガーされるxargs
SIGCHLDでジョブプールを起動して管理できます。trap
(
todo=( {1..1000} ) max=100
TRAPCHLD() {
while (( $#jobstates < max && $#todo )); do
foo $todo[1] & shift 1 todo
done
}
: start &
while (( $#todo )) wait
)
ここでは、新しいタスクのリストを取得するためにサブシェルでそれを実行する必要があります。 TRAPCHLDトラップの実行中にSIGCHLDがブロックされるため、トラップはそれ自体を再入力しないでください。これにより、競合状態やリストへの同時アクセスを防止する必要性を回避する必要があります$todo
。
答え2
グループとして実行できる場合は、ループを入れ子にします。
#! /bin/bash
date '+%T.%N'
for j in {1..3}; do
for k in {1..3}; do
(( ++i ))
( sleep 2.0 && printf 'Foo %d\n' $i ) &
done
wait
date '+%T.%N'
printf 'Batch %d ends\n' $j
done
date '+%T.%N'
結果は時間の重複を示しています。
$ ./aBatch
19:55:17.078476713
Foo 1
Foo 2
Foo 3
19:55:19.094302514
Batch 1 ends
Foo 4
Foo 6
Foo 5
19:55:21.114530543
Batch 2 ends
Foo 7
Foo 9
Foo 8
19:55:23.132184671
Batch 3 ends
19:55:23.135792952
$
これはGNU並列性でも同様です。この方法の利点は、実行が異なる時間に実行されると、バッチ処理の他のparallel
プロセスを待つ必要なしに追加のプロセスが開始されることです。
#! /bin/bash
#.. The script ./aFoo
sleep 2 && printf 'Foo %d\n' $1
注文する:
$ date '+%T.%N'; parallel -j 3 ./aFoo -- {1..9}; date '+%T.%N'
20:11:44.446042653
Foo 3
Foo 1
Foo 2
Foo 4
Foo 5
Foo 6
Foo 7
Foo 8
Foo 9
20:11:50.503324162
$
答え3
以下は100単位に分割する簡単な方法です。 (bash)
for i in {1..1000}
do
foo "$i" &
(( i % 100 )) || wait
done
wait
同じシェルで実行される他のバックグラウンドジョブがないとし、100個のジョブを開始し、すべてのジョブが完了するのを待ち、もう100個のジョブを開始し、最後に残りのジョブが完了するのを待ちます。 (1000と100は残りがありませんが、他の場合はあります)
++n
ループ変数が数値でない場合は、代わりに式で使用できますi
n=0
for i in *
do
foo "$i" &
(( ++n % 100 )) || wait
done
wait
答え4
GNU Parallelはこのような状況のために作られました:
parallel foo ::: {1..1000}
foo n
1000個のジョブがすべて実行されるまで、CPUスレッドごとに1つずつ(n = 1..1000)実行されます。あるジョブが完了すると、別のジョブが開始されます。
parallel -j100 foo ::: {1..1000}
foo n
1000個のジョブがすべて実行されるまで、100個(ここでn = 1..1000)が実行されます。
GNU Parallel は出力を直列化するので、2 つのfoo
s が同時に印刷されても出力は歪ませません。
GNU Parallelには、並列化を容易にする他の多くの機能があります。 1章と2章を読むのに20分かかります。https://zenodo.org/record/1146014あなたのコマンドラインが感謝します。