web-dev-qa-db-ja.com

4つのタスクを並行して実行する...どうすればよいですか?

ディレクトリにたくさんのPNG画像があります。これらの画像を圧縮するために実行するpngoutというアプリケーションがあります。このアプリケーションは、私が実行したスクリプトによって呼び出されます。問題は、このスクリプトが一度に1つずつ実行することです。

FILES=(./*.png)
for f in  "${FILES[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 $f R${f/\.\//}
done

一度に1つのファイルのみを処理すると、多くの時間がかかります。このアプリを実行した後、CPUがわずか10%であることがわかります。したがって、これらのファイルを4つのバッチに分割し、各バッチをディレクトリに配置し、4つのターミナルウィンドウ、4つのプロセスから4つを起動できることを発見しました。それで、スクリプトの4つのインスタンスがあり、同時にこれらの画像と仕事は時間の1/4かかります。

2番目の問題は、画像とバッチを分割し、スクリプトを4つのディレクトリにコピーし、4つのターミナルウィンドウを開き、何とか時間を無駄にしたことです...

何も分割せずに、1つのスクリプトでそれを行うにはどうすればよいでしょうか。

つまり、最初にbashスクリプトからプロセスをバックグラウンドで実行するにはどうすればよいですか。 (最後に&を追加するだけですか?)2番目:4番目のタスクを送信した後、バックグラウンドへのタスクの送信を停止し、タスクが終了するまでスクリプトを待機させるにはどうすればよいですか?つまり、1つのタスクの終了時に新しいタスクをバックグラウンドに送信し、常に4つのタスクを並行して実行しますか?そうしないと、ループが何十億ものタスクをバックグラウンドで実行し、CPUが詰まります。

23
SpaceDog

-Pとの並列実行をサポートするxargsのコピーがある場合、簡単に行うことができます

printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}

他のアイデアについては、Wooledge Bash wikiのProcess Management記事に section があり、必要なものを正確に説明しています。

33
jw013

すでに提案されているソリューションに加えて、非圧縮から圧縮ファイルを作成する方法を記述したメイクファイルを作成し、make -j 4を使用して4つのジョブを並行して実行できます。問題は、圧縮ファイルと非圧縮ファイルに異なる名前を付けるか、別のディレクトリに保存する必要があることです。そうしないと、妥当なmakeルールを作成できなくなります。

8
9000

GNU Parallel http://www.gnu.org/software/parallel/ がインストールされている場合、これを行うことができます:

parallel ./pngout -s0 {} R{} ::: *.png

GNU Parallelは次の方法で簡単にインストールできます。

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

GNU Parallelの紹介ビデオをご覧ください:詳細は https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

7
Ole Tange

2つの質問に答えるには:

  • はい、行の最後に&を追加すると、シェルにバックグラウンドプロセスを起動するように指示されます。
  • waitコマンドを使用すると、バックグラウンドですべてのプロセスが完了するのを待ってから先に進むようにシェルに要求できます。

以下は、jを使用してバックグラウンドプロセスの数を追跡するように変更されたスクリプトです。いつ NB_CONCURRENT_PROCESSESに達すると、スクリプトはjを0にリセットし、すべてのバックグラウンドプロセスが完了するのを待ってから、実行を再開します。

files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 "$f" R"${f/\.\//}" &
        ((++j == nb_concurrent_processes)) && { j=0; wait; }
done
5