• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:CGIから複数の別プログラムを同時に呼び出す)

複数の別プログラムを同時に呼び出す方法

このQ&Aのポイント
  • Web上の複数の画像を効率的に取得する方法を紹介します。画像を順に取り込むのではなく、複数のプログラムを同時に起動して並列処理を行うことで時間短縮が可能です。
  • 一つずつ画像を取得してからHTMLを表示させるのに時間がかかる場合、wgetでファイルを取りに行くサブルーチン部分を別に作り、複数のプログラムを同時に起動して並列処理を行いましょう。
  • system()関数では親プログラムが子プログラムの終了を待つため、効率的な並列処理ができません。代わりに、プロパティを渡して個別にプログラムを起動する方法を採用すると良いでしょう。

質問者が選んだベストアンサー

  • ベストアンサー
  • shiren2
  • ベストアンサー率47% (139/295)
回答No.5

Parallel::ForkManagerというモジュールがあります。 やっていることはただのforkですけどね。 #!/usr/bin/perl use strict; use Parallel::ForkManager; my @url = qw/url_a url_b/; my $pm = Parallel::ForkManager->new(20); #同時実行の数 for(@url){   $pm->start and next;   system("wget $_ >& /dev/null");   $pm->finish; } $pm->wait_all_children; もし速度が重要なら、画像取得後にHTMLを出力するのではなく、先に出力してからJavaScriptで動的に表示したほうがいいですよ。

参考URL:
http://search.cpan.org/~dlux/Parallel-ForkManager/ForkManager.pm
AAbb11ccDD
質問者

補足

モジュールがあるんですね。 これは楽ちんでいいです。 ありがとうございました。 > もし速度が重要なら、画像取得後にHTMLを出力するのではなく、先に出力してから > JavaScriptで動的に表示したほうがいいですよ。 画像取得後に <img src="abc.jpg"> と出力するよりも、 先に <SCRIPT language="JavaScript"> <!-- document.write("<img src=\"abc.jpg\">"); //--> </SCRIPT> みたいに書いたHTMLを出力しちゃったほうがいいということでしょうか? Javascriptがよく分かっていないのですが、こうするとabc.jpgを取得し終わる前に ブラウザにHTMLソースを送ってしまっても大丈夫てことなんでしょうか? 初歩的な質問で恐縮ですが、よろしくご教授ください。

その他の回答 (6)

  • shiren2
  • ベストアンサー率47% (139/295)
回答No.7

連投してすみません。 汚染に注意とは書いておいたものの、それだけで大丈夫か不安になったので、より安全なバージョンを書いておきます。 ANo.6のコードは大変危険ですので、決してそのまま設置しないでください。 #!/usr/bin/perl use strict; # 汚染に注意 my $fname = $ENV{'QUERY_STRING'}; $fname =~ s/[^0-9a-zA-Z.]/\_/g; my $url = "http://www.hogehoge.com/" . $fname; print "Content-type: image/jpeg\n\n"; # サーバーに保存せず直接表示 system(qq/wget -q -O- $url/); # サーバーに保存しつつ表示 #system(qq/wget -q -O- $url | tee $fname/);

AAbb11ccDD
質問者

お礼

いろいろと教えて頂きありがとうございました。 平均30Kbの画像30個を一つずつ順に取りに行くオリジナルバージョンが、20秒程度かかっていたのが、No4のfork関数やNo5の Parallel::ForkManagerモジュールを使ったバージョンでは劇的に速くなって2秒以下で表示されるようになりました。 このNo6のバージョンはまだ試していませんが、これが速度的には一番ぽいですね。 ありがとうございました。

  • shiren2
  • ベストアンサー率47% (139/295)
回答No.6

>ブラウザにHTMLソースを送ってしまっても大丈夫てことなんでしょうか? HTMLの表示後に、JavaScriptで動的に画像を取得するという意味です。 技術的に難しいようなら、無理に実装しなくてもいいと思います。 ところで、仕様的に可能であるなら画像の表示部分をCGIにしてみてはいかがでしょう。 速度的には最速だと思います。 <img src="view.cgi?abc.jpg"> #!/usr/bin/perl use strict; # 汚染に注意 my $fname = $ENV{'QUERY_STRING'}; my $url = "http://www.hogehoge.com/" . $fname; print "Content-type: image/jpeg\n\n"; # サーバーに保存せず直接表示 system("wget -q -O- $url"); # サーバーに保存しつつ表示 #system("wget -q -O- $url | tee $fname");

回答No.4

表示がずれるので、空白2文字を全角空白で書いていることに注意 use strict; use warnings; use POSIX 'strftime'; print '### start  : ', strftime("%Y-%m-%d %H:%M:%S", localtime), $/; my $pid = undef; for my $count (0 .. 9) {   $pid = fork;   if ( !defined $pid ) {     die "Error Fork : $pid";   }   elsif ($pid == 0) {     # child process     print "$count sleep ... : ", strftime("%Y-%m-%d %H:%M:%S", localtime), $/;     sleep 3;     print "$count wakeup  : ", strftime("%Y-%m-%d %H:%M:%S", localtime), $/;     exit;   } } # wait all child process while ((my $wait_pid = wait) != -1) {} print '### end   : ', strftime("%Y-%m-%d %H:%M:%S", localtime), $/; --- 結果 $ perl -w wait.pl ### start  : 2010-10-30 23:11:45 2 sleep ... : 2010-10-30 23:11:45 3 sleep ... : 2010-10-30 23:11:45 4 sleep ... : 2010-10-30 23:11:45 5 sleep ... : 2010-10-30 23:11:45 6 sleep ... : 2010-10-30 23:11:45 7 sleep ... : 2010-10-30 23:11:45 8 sleep ... : 2010-10-30 23:11:45 9 sleep ... : 2010-10-30 23:11:45 0 sleep ... : 2010-10-30 23:11:45 1 sleep ... : 2010-10-30 23:11:45 2 wakeup  : 2010-10-30 23:11:48 3 wakeup  : 2010-10-30 23:11:48 4 wakeup  : 2010-10-30 23:11:48 6 wakeup  : 2010-10-30 23:11:48 7 wakeup  : 2010-10-30 23:11:48 8 wakeup  : 2010-10-30 23:11:48 9 wakeup  : 2010-10-30 23:11:48 5 wakeup  : 2010-10-30 23:11:48 0 wakeup  : 2010-10-30 23:11:48 1 wakeup  : 2010-10-30 23:11:48 ### end   : 2010-10-30 23:11:48

AAbb11ccDD
質問者

補足

実行結果まで書いていただきありがとうございました。 fork()関数てのがあるんですね。 調べてみたのですが、こういう理解でいいのでしょうか。 $pid = fork; という一文だけで、プログラム自体が丸ごと複製され、子プログラムとして起動し(但し、子プロセスの処理の開始は、fork 直後から)、$pidには親プロセスなら子のプロセスID、子なら 0 、コピーに失敗した場合は未定義値が入る。 だから、$pidの中身を調べると自分が親なのかコピーされたクローンなのかをプログラム自身が知ることになる。 > elsif ($pid == 0) { でもって自分が子ならそれぞれ独立して与えられた仕事をこなし、終了したらexitする。 > while ((my $wait_pid = wait) != -1) {} 親なら全ての子プロセスが終了するまで待って、残りの作業を片付ける。

回答No.3

No.2で回答した者です。先ほどは間違えました。 > $x=0; > while (`ps | awk '/downloader/&&!/awk/{a++};END{print a}'` > 0 || $x < 60) { これは "while (`.....` > 0 && $x < 60) { ..."とすべきところでした。訂正します。 No.1回答者様がおっしゃっているように、wgetの引数で複数サイトを一度に指定できる場合は、その方が簡便な解決策になるでしょう。同時平行ダウンロードはできませんが、CGIを待たせるコード、タイムラグが不要になります。旧バージョンのwgetではできないみたいなので、場合によりwget、curlなどのアップグレードインストールが必要になるかもしれません。

AAbb11ccDD
質問者

補足

ご丁寧な解説ありがとうございました。 そういえば UNIXコマンドで & を付けてバックグラウンドで走らせるというのがありましたね。 すっかり忘れてしまっています(^^;; $x=0; while (`ps | awk '/downloader/&&!/awk/{a++};END{print a}'` > 0 && $x < 60) { sleep 1; $x++; } これは、バックグラウンドで起動させた複数の「downloader」という名前のプログラムのプロセス数を awk で数えて変数「a」に加えていき、最後にEND{print a}で出力させて、それが0個になるまで1秒ごとに最大60回まで調べに行くということでいいでしょうか?

回答No.2

こんにちは。おっしゃりたいことを正確に理解しているかどうか自信がないのですが、 > 結局親プログラムは呼び出した一つの子プログラムの終了まで待って次に行くみたいなので意味ないかと・・・。 Linux (Kernel 2.6.21.7) + Perl 5.8.8で以下のように実行すると、xtermが3つ連続起動した後にperlが終了します。バックグラウンドで起動しますので、xtermの終了を待たずにperlは終了します。 perl -e 'for(1..3){system("xterm &")}' ですので、system()でダウンロードプロセスを起動する際に&を後ろに付けてバックグラウンドに回せば、CGIはそのまま次の動作に移るのではないかと思います。ただし、複数起動させたダウンロードプロセスの終了をCGIが待たなければならないと思いますので、ダウンロードプロセス起動後に $x=0; while (`ps | awk '/downloader/&&!/awk/{a++};END{print a}'` > 0 || $x < 60) { sleep 1; $x++; } などとして、CGIを一時停止する必要があるかもしれません。上記コードでは最大60秒間停止することになります。

  • notnot
  • ベストアンサー率47% (4900/10358)
回答No.1

可能ですが、すべての取得が終わったことを待ち合わせないといけないので、すこし難しくなります。「こういうことが可能か?」と質問するレベルのスキルでは難しいと思います。 wgetの引数に複数のURLを指定出来るので、複数指定すると1ファイルずつwgetするよりはずいぶん速くなります。平行して複数を取得する訳じゃないですが、wgetの起動が一度で済むので。

AAbb11ccDD
質問者

お礼

> すべての取得が終わったことを待ち合わせないといけないので、すこし難しくなります。 確かに初心者には難しそうですね。 でも可能だとわかったので、勉強してみようと思います。 ありがとうございました。