分散オブジェクト環境を学ぶ
連載:HORBと遊ぼう(6)

やさしく学ぶ並列プログラミング


(2)いよいよ並列プログラミング

 HORBの非同期メソッドと同時実行性の保証について説明してきました。これでパラレル(並列)プログラミングを行うための基礎知識が得られたと思います。さあ、いよいよパラレルプログラミングにチャレンジしましょう。

■複数台のサーバのオブジェクトを実行

 パラレルプログラミングの例題としては、複数台のサーバを立ち上げて、それぞれのサーバに配置されたリモートオブジェクトにメソッド処理を並列に実行させ、処理が終わったものの順にコールバックによりクライアントに結果を出力させるような内容にしています。

 この例題で取り上げるシステム構成は以下のとおりです。マシンは1台で利用できるように、ポート番号を指定し、HORBサーバを複数台立ち上げることで、仮想サーバを実現します。処理イメージをオブジェクト図で表します(図8)

図8 処理イメージ

 ソースコードは、Client6.java(リスト9)です。コード中にホスト名とポート番号がハードコーディングされています(リスト9の5、6行目)ので実際に複数のマシンで試してみたいときは、この部分を変更してください。

001:import horb.orb.*;
002:
003:public class Client6 implements AsyncMethodHandler {
004:
005:  static String[] host = {"localhost:8000",
006:  "localhost:8001","localhost:8002"};
007:  int tasks[] = {5000, 2000, 3000, 1000, 4000, 3000,
008:  2000, 4000, 3000, 1000, 2000, 6000};
009:  Remote3_Proxy servers[];
010:  Object end = new Object();
011:  int start=0;
012:  public static void main(String args[]) {
013:    Client6 client = new Client6();
014:    client.test();
015:  }
016:
017:  void test() {
018:    servers = new Remote3_Proxy[host.length];
019:    for (int i = 0; i < host.length; i++) {
020:      servers[i] = new Remote3_Proxy("horb://"+host[i]);
021:    }
022:    int task=0;
023:    int hostNo=0;
024:    do{
025:      servers[hostNo]._setHandler(this, task);
026:      enterTask(hostNo++,task++);
027:      if( hostNo ==host.length )
028:        hostNo=0;
029:      }while(task < tasks.length);
030:
031:      System.out.println("All tasks have done.");
032:      try{ Thread.sleep(10000); }catch(Exception ex){}
033:    }
034:
035:    synchronized void enterTask(int hostid,int task) {
036:    System.out.println("["+hostid+"]番目のホストに依頼:ホスト名="+host[hostid]);
037:    ResultAsync rc = servers[hostid].longJob_Request(tasks[task]);
038:  }
039:
040:  public synchronized void run(ResultAsync ra, int tag) {
041:      System.out.println("["+tag+
042:      "]回目のサーバ処理 処理時間["+ra.receiveInt()+"]ms.");
043:  }
044:}
リスト9 Client6.java [examples\step9\]

 このソースコードで新たに説明すべき点は以下のとおりです。

●20行目……サーバにリモートオブジェクトを生成する
 localhost、ポート8000、8001、8002にリモートオブジェクトを生成します。

●24〜29行目……仕事(longJobメソッド)を各サーバに振り分ける
 この部分で、各サーバに生成されたリモートオブジェクトの非同期メソッドをリクエストしています。この作業は7行目のtasks配列分すべてのタスクを一挙に振り分けます。25行目の_setHandlerメソッド呼び出しは、コールバックメソッドrun(40行目)が呼ばれた際に、何回目のタスク呼び出しか判断するために毎回セットしています。

■実行結果

 図9に実行結果を示します。いかがでしょうか。図9では、表示順は省略していますが、複数のサーバにパラレルに振り分けられたlongJobメソッドの処理結果が、終了した順にクライアントに返却されてきているのがお分かりになると思います。このようにパラレルプログラミングによっていろんな仕事を複数のマシンに割り当て、並列に処理することができるようになるのです。

  C:\examples\step9>java Client6
  [0]番目のホストに依頼:ホスト名=localhost:8000
  [1]番目のホストに依頼:ホスト名=localhost:8001
  [2]番目のホストに依頼:ホスト名=localhost:8002
  [0]番目のホストに依頼:ホスト名=localhost:8000
  [1]番目のホストに依頼:ホスト名=localhost:8001
  [2]番目のホストに依頼:ホスト名=localhost:8002
  [0]番目のホストに依頼:ホスト名=localhost:8000
  [1]番目のホストに依頼:ホスト名=localhost:8001
  [2]番目のホストに依頼:ホスト名=localhost:8002
  [0]番目のホストに依頼:ホスト名=localhost:8000
  [1]番目のホストに依頼:ホスト名=localhost:8001
  [2]番目のホストに依頼:ホスト名=localhost:8002
  All tasks have done.
  [3]回目のサーバ処理 処理時間[1000]ms.
  [9]回目のサーバ処理 処理時間[1000]ms.
  [10]回目のサーバ処理 処理時間[2000]ms.
  [1]回目のサーバ処理 処理時間[2000]ms.
  [6]回目のサーバ処理 処理時間[3000]ms.
  [2]回目のサーバ処理 処理時間[3000]ms.
  [5]回目のサーバ処理 処理時間[3000]ms.
  [8]回目のサーバ処理 処理時間[3000]ms.
  [4]回目のサーバ処理 処理時間[4000]ms.
  [7]回目のサーバ処理 処理時間[4000]ms.
  [0]回目のサーバ処理 処理時間[5000]ms.
  [11]回目のサーバ処理 処理時間[6000]ms.

  C:\examples\step9>horb -port 8002
  サーバ処理(1000ms)の開始
  サーバ処理(2000ms)の開始
  サーバ処理(1000ms)の開始
  サーバ処理(5000ms)の開始
  サーバ処理(1000ms)の終了
  サーバ処理(1000ms)の終了
  サーバ処理(2000ms)の終了
  サーバ処理(5000ms)の終了

  C:\examples\step9>horb -port 8001
  サーバ処理(2000ms)の開始
  サーバ処理(4000ms)の開始
  サーバ処理(2000ms)の開始
  サーバ処理(2000ms)の開始
  サーバ処理(2000ms)の終了
  サーバ処理(2000ms)の終了
  サーバ処理(4000ms)の終了
  サーバ処理(4000ms)の終了

  C:\examples\step9>horb -port 8000
  サーバ処理(3000ms)の開始
  サーバ処理(3000ms)の開始
  サーバ処理(3000ms)の開始
  サーバ処理(6000ms)の開始
  サーバ処理(3000ms)の終了
  サーバ処理(3000ms)の終了
  サーバ処理(3000ms)の終了
  サーバ処理(6000ms)の終了
図9 実行結果

 今回は、やさしいパラレルプログラミングということで、パラレルプログラミングに親しんでいただきました。実際には、サーバ間の通信例外をcatchしたりする設計が必要となります。

 また、この例題では、生成されたリモートオブジェクトの負荷状態を無視して無条件に仕事(longJobメソッド)を依頼していますが、もっと、まともに設計するとすれば、サーバ負荷とSocket資源(注)を考慮したものにしなければなりません。特に、同時呼び出しが非常に多くなる可能性がある場合、Socket資源についてはできるだけ消費しないように非同期メソッド呼び出しのタイミングを設計しなければなりません。

 具体的には、サーバ側の処理を終え、コールバックしてきたホストだけに次の仕事を依頼する方法などがあります。そうすれば非同期用のSocketは1本しか消費されません。または、1つのサーバに依頼する仕事の個数を限定するとよいでしょう(これを実現するには、サーバ側の代理オブジェクトの個数で調整すると簡単です)。前者のよい例題としては、HORBパッケージのexamples\async\Client6.javaにありますので、参考にしてみてください。また、このディレクトリには非同期メソッドのさまざまな使い方が説明されていますのでとても参考になると思います。

(注)HORBは非同期呼び出しを行う際に、並行処理を実行している分だけSocketを使っています。非同期メソッドが完了するとそのSocketは再利用されたり、長時間必要とされなければ解放されたりするようになっています。

 では、この辺で今回は終わりたいと思います。HORB通じて、パラレルプログラミングの世界を楽しんでいただけましたか? 次回は、いよいよ最終回となります。お楽しみに!!

 

Index

第6回 やさしく学ぶ並列プログラミング

  (1)非同期メソッドを理解する
 非同期メソッド -OneWay呼び出し-
 戻り値をもつ非同期メソッド呼び出し -Asyncメソッド-
 Asyncメソッドのコールバック呼び出し
(2)いよいよ並列プログラミング
 複数台のサーバのオブジェクトを実行
 実行結果
 

連載記事一覧




Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間