次のbashスクリプトがあります。
for i in {0800..9999}; do
for j in {001..032}; do
wget http://example.com/"$i-$j".jpg
done
done
すべての写真が存在し、実際、各反復は別の反復に依存していません。スレッド数を制御できるように並列化するにはどうすればよいですか?
Confiqの答えは、小さいi
とj
に適しています。ただし、質問でのi
とj
のサイズを考えると、生成されるプロセスの総数を制限する必要があるでしょう。これは、parallel
コマンドまたはxargs
の一部のバージョンで実行できます。たとえば、-P
フラグをサポートするxargsを使用すると、次のように内部ループを並列化できます。
for i in {0800..9999}; do
echo {001..032} | xargs -n 1 -P 8 -I{} wget http://example.com/"$i-{}".jpg
done
GNU parallel には、より高度な動作が必要な場合に備えて多数の機能があり、両方のパラメーターを簡単に並列化できます。
parallel -a <(seq 0800 9999) -a <(seq 001 032) -P 8 wget http://example.com/{1}-{2}.jpg
for i in {1..3}; do
for j in {10..20}; do
(wget http://example.com/"$i-$j".jpg &)
done
done
私もそれをテストしました...
これは非常に単純なアプローチです。この例では、スレッドを10に制限します。
for i in {0800..9999}; do
for j in {001..032}; do
wget http://example.com/"$i-$j".jpg &
while test $(jobs -p|wc -w) -ge 10; do sleep 0.1 ; done
done
done
parallel
が使用できない環境で同じ問題を解決したおおまかな方法は次のとおりです。これはbash機能に依存しているため、_#!/bin/bash
_またはbashを介してスクリプトを明示的に実行する必要があります。
_MAX_CONCURRENT=50
n=0
some_command_that_outputs_urls \
| while read url
do
{
do_something_with $url
} &
PIDS="$PIDS $!"
((++n))
if test "$n" -ge "$MAX_CONCURRENT"
then
n=0
wait $PIDS
PIDS=""
fi
done
test -n "$PIDS" && wait $PIDS
_
_$MAX_CONCURRENT
_を調整して、必要な(概算の)最大スレッド数を指定できます。そしてもちろん、_some_command_that_outputs_urls
_と_do_something_with $url
_を、シナリオにふさわしいものに置き換えます。たとえば、_some_command_that_outputs_urls \
_という行を次のように置き換えます。
_for i in {0800..9999}; do
for j in {001..032}; do
printf 'http://example.com/%s-%s.jpg\n' $i $j
done
done \
# ...| while read url ...
_
および_do_something_with $url
_
_wget $url
_
あなたに最終結果を与える
_MAX_CONCURRENT=50
n=0
for i in {0800..9999}; do
for j in {001..032}; do
printf 'http://example.com/%s-%s.jpg\n' $i $j
done
done \
| while read url
do
{
wget $url
} &
PIDS="$PIDS $!"
((++n))
if test "$n" -ge "$MAX_CONCURRENT"
then
n=0
wait $PIDS
PIDS=""
fi
done
test -n "$PIDS" && wait $PIDS
_
これが機能する方法は、標準出力に(この場合は)URLのリストを生成し、その行を一度に1行ずつwhile
ループに読み込むコマンドです(改行に注意してください!)。 _$MAX_CONCURRENT
_同時プロセスまで生成され、_$n
_を使用して生成された数を追跡し、_$PIDS
_を使用してプロセスIDを記録します。 _$MAX_CONCURRENT
_プロセスが生成されると(実際に生成されるのは複合ステートメントであるため、その中に複数のコマンドとブロックを含めることができます)、生成されたPIDでwait
になります(これにより、指定されたPIDのいずれもまだ実行中でない場合は直ちに)、その内部状態をリセットしてから、別の実行を続行します。
再利用されたPIDの処理を改善するなど、このスクリプトを改善する方法はいくつかありますが、実行する必要がある環境で実行したかったので、私には十分です。私の実際のバージョンでは、タイムアウトも設定されており、cronを介して定期的に再実行されるため、この単純なバージョンと比較して、暴走の実行時間のリスクが大幅に減少します。