ガイドライン:
|
方式 |
利点 |
欠点 |
単一プロセス、スレッドなし |
|
|
単一プロセス、マルチスレッド |
|
|
マルチプロセス |
|
|
典型的な発展経過は、単一プロセス アーキテクチャから出発し、同時性を必要とする動作のグループにプロセスを追加していくというものです。このような広いグループの中で、さらに並行性の必要性について検討し、プロセス内にスレッドを追加して並行性を拡大します。
最初の出発点は、目的に合わせて作られたアクティブ オブジェクト スケジューラを使用して、オペレーティング システムの 1 つのタスクまたはスレッドに、多数のアクティブ オブジェクトを割り当てることです。これにより、通常は、非常に軽量な並行性のシミュレーションを達成できますが、オペレーティング システムの単一のタスクやスレッドを使用するので、複数 CPU によるメカニズムの利点を活用することはできません。独立したスレッド内で起きるブロッキング動作を切り離して、ブロッキング動作がボトルネックにならないようにすることが重要な決定となります。結果として、ブロッキング動作を伴うアクティブ オブジェクトを分離し、専用のオペレーティング システム スレッドにします。
残念なことに、アーキテクチャ上の多くの決定と同様に、簡単な答えはありません。正しいソリューションには、注意深くバランスの取れた方法が必要です。アーキテクチャに関する小規模なプロトタイプを使用して、特定の選択の影響を調査できます。プロセス アーキテクチャのプロトタイプを作る場合には、プロセスの数をシステムに対する理論的な上限まで増やすことに焦点を当てます。以下の問題点を検討します。
アクティブなオブジェクト同士は、同期または非同に期通信を行うことができます。同期通信は、厳密に順序を制御することによって複雑なコラボレーションを単純化でき、便利です。つまり、あるアクティブなオブジェクトが、ほかのアクティブ オブジェクトを同期して呼び出す必要のある「RTC 列」を実行している間は、ほかのオブジェクトが開始したいかなる並行動作の相互作用も、そのシーケンスが完全に完了するまでは無視できます。
これは役に立つ場合もある一方で、より重要な高優先度のイベントが待たされることもある (優先度の逆転現象) ため、問題もあります。同時に呼び出されたオブジェクトそのものが、自分自身の同期呼び出しへの応答待ちでブロックされる可能性があるため、事態はさらに悪化します。これは、無制限な優先度逆転現象につながる可能性があります。最も極端なケースでは、同期呼び出しのチェーンに循環性があると、デッドロックにつながる可能性もあります。
非同期呼び出しでは、応答時間を制限できるようにすることで、この問題を回避しています。ただし、ソフトウェア アーキテクチャによっては、非同期通信はしばしばより複雑なコードにつながることがあります。これは、アクティブ オブジェクトは複数の非同期イベントにいつでも対応しなければならないためです (これらの各非同期イベントによって、ほかのアクティブ オブジェクトとの間で非同期反復の複雑な順序が強いられることがあります)。これは非常に困難で、実装時エラーの温床となります。
メッセージ配信が保証されている非同期メッセージング技術を使用すれば、アプリケーションのプログラミング作業が簡単になります。アプリケーションは、ネットワーク接続またはリモート アプリケーションが利用できなくなった場合でも、処理を続行できます。非同期メッセージングは、同期モードでもその使用を妨げられません。同期技術では、アプリケーションが利用可能なときには常に接続が利用可能でなければなりません。接続が存在することがわかっていれば、コミット処理がより簡単になります。
アクティブ オブジェクトのコンテキスト切り替えのオーバーヘッドは非常に小さい場合がありますが、アプリケーションによっては、そのコストでさえ受け入れられない可能性があります。通常、これは、大量のデータ処理を高速に行う必要がある状況で発生します。このような場合には、受動オブジェクトや、セマフォのようなさらに伝統的な (ただしリスクの高い) 並列管理技法を使うような状態に、後退させられることがあります。
ただし、このような検討をしても、アクティブ オブジェクトのアプローチを完全に捨て去る必要があることを意味するわけではありません。このようなデータ中心のアプリケーションでも、システム全体から見れば、性能を気にする部分は相対的に小さい場合が多いものです。つまり、システムの残りの部分では、アクティブ オブジェクトのパラダイムをまだ活用できるということです。
システム設計という観点からみると、一般に、性能は設計基準の 1 つにしか過ぎません。システムが複雑になると、保守性、変更の容易さ、理解しやすさなどの別の基準も、より重要ではないにしても、同じ程度に重要です。アクティブ オブジェクトのアプローチは、明確に優れた点を備えています。このアプローチは、並行性と並行性管理の複雑さのほとんどを隠蔽する一方で、低レベルの技術特有のメカニズムではなく、アプリケーション固有の用語で設計を表現できるようにするからです。
相互作用のない並行コンポーネントは、ほとんど取るに足らない問題です。ほとんどすべての設計上の問題が並行活動間の相互作用と関係しているため、まず、相互作用を理解することにエネルギーを集中させる必要があります。次のような質問をします。
相互作用について理解したら、これを実装する方法について考えることができます。実装方法としては、システムの性能目標に合致する最も単純な設計を産み出すものを選択する必要があります。一般に、性能要求には、全体的なスループットと、外部生成イベントに許容範囲内の遅れで対応できることが含まれます。
外部インターフェイスに関する特定の仮定を、アプリケーションのあちこちに埋め込むのは悪い慣習です。また、複数の制御スレッドをイベント待ちにしてブロックするのは非効率的です。代わりに、イベント検出専用タスクを 1 つのオブジェクトに割り当てます。イベントが発生すると、そのオブジェクトは、イベントの発生を知る必要があるほかのオブジェクトに通知できます。この設計は、よく知られていて認知されている設計パターンである、「観察者」パターンに基づいています [GAM94]。これを、より柔軟性の高い「パブリッシャ - サブスクライバ パターン」に、簡単に拡張できます。ここで、パブリッシャ オブジェクトは、イベント検出者とイベントに興味のあるオブジェクト (「サブスクライバ」) との間の仲介者として働きます [BUS96]。
システムにおけるアクションは、外部で生成されたイベントの発生によってトリガされます。非常に重要な外部生成イベントの 1 つは単純な時間の経過で、クロックの時の刻みで表されます。その他の外部イベントは、ユーザー インターフェイス装置、プロセス センサー、ほかのシステムへの通信リンクなどのような外部ハードウェアに接続されている、入力デバイスからのものです。
ソフトウェアがイベントを検出するには、割り込み待ちにして動きを止めておくか、またはイベントが発生していないかどうかを確認するため、定期的にハードウェアをチェックする必要があります。後者の場合には、定期的なサイクルを短くして、短命なイベントの見落としや多重発生を防いだり、単にイベントの発生と検出の間の遅延を最小にしたりすることが、必要になる場合があります。
このことに関して興味深いのは、どんなにイベントの発生が稀であっても、イベントの発生を待機したり、頻繁にチェックしたりして、何らかのソフトウェアをブロックしなければならないことです。しかし、システムが処理する必要のあるイベントの多数 (ほとんどでない場合) は稀にしか発生しません。どんなシステムでも、大部分の時間は、重要なことはほとんど何も発生しません。
エレベータ システムは、このよい例を多数提供してくれます。エレベータの運転の中で重要なイベントには、エレベータ サービスの呼び出し、乗客による階の選択、乗客が手でドアを遮る動作、ある階から次の階への通過などがあります。これらのイベントの中には非常に俊敏な応答が必要なものがありますが、望ましい応答時間の規模と比べるとすべてがきわめて稀なイベントです。
1 つのイベントが多数のアクションを引き起こしたり、アクションが各種オブジェクトの状態に依存したりすることがあります。さらに、システムの構成が異なれば、イベントが同じでも使い方が異なる場合があります。たとえば、エレベータがある階を通過するときには、エレベータ ケージの中の表示を更新し、エレベータ自身が自分の場所を知って、新しい呼び出しがかかった場合や乗客が降りる階を選択した場合の応答方法を知る必要があります。各フロアには、エレベータの現在の場所を表示するものがある場合とない場合があります。
ポーリングは大きな代償を必要とします。これを実現するには、システムの一部の動作を定期的に停止して、イベントが発生しているかどうかをチェックする必要があります。イベントに迅速に応答する必要がある場合には、システムは非常に頻繁にイベントの到着をチェックする必要があり、システムが達成できる仕事量にさらに制約を加えます。
イベントに割り込みを割り当て、割り込みによってイベント依存のコードを起動させる方が、はるかに効率的です。割り込みも「高価」と考えられるので、割り込みを避ける場合がありますが、ほどよく割り込みを使う方がポーリングを繰り返すよりはるかに効率的です。
イベント通知メカニズムとして割り込みの方が好まれるケースとしては、イベントの到着がランダムでたまにしか起きず、ポーリング作業のほとんどがイベント未発生の結果に終わるような場合です。ポーリングの方が好まれるケースは、イベントが定期的かつ予想可能な形で起きる場合で、ポーリング作業のほとんどでイベント発生を見つけられる場合です。この中間で、ポーリングでも反応型動作でもどちらでもかまわない場合があります。どちらでもうまくいき、選択にあまり重要性がない場合です。ただし、実世界で起きるイベントのランダム性を考えると、ほとんどのケースでは反応型動作が適しています。
データのブロードキャストは (通常はシグナルを使う)、多くのリソースを必要とします。また、たいていは無駄が多くなります。そのデータに興味があるのはほんの一握りのオブジェクトですが、全部 (または多数) が作業を停めて、そのデータを調べる必要あります。よりよい、リソースの浪費の少ない方法は、あるイベントが発生することに興味があるオブジェクトだけに知らせる通知を使うことです。イベントのブロードキャストは、多数のオブジェクトの注意を促す必要のあるイベント (通常は、タイミング イベントまたは同期イベント) だけに限定します。
より具体的には次のようになります。
おそらく、効率的な並行アプリケーションを開発するのに最も重要なガイドラインは、最軽量の並行メカニズムを最大限に使用することです。並行性をサポートする上で、ハードウェアとオペレーティング システム ソフトウェアは両方とも主要な役割を果たしますが、共にかなりの重量メカニズムであって、アプリケーション設計者がやるべきことがたくさん残ります。利用可能なツールと、並行アプリケーションのニーズとの間の大きなギャップを埋める必要があります。
アクティブ オブジェクトは、このギャップを次の 2 つの中心となる特性で埋める役目をします。
アクティブ オブジェクトは、プログラミング言語が提供する受動オブジェクトにとっても理想的な環境です。プログラムやプロセスのような手続き的成果物を使わずに、並行オブジェクトの基本からシステム全体を設計すると、モジュール性が高く、まとまりがあり、理解しやすい設計につながります。
ほとんどのシステムでは、コード全体の 10% 未満が CPU サイクルの 90% 以上を使用します。
システム設計者の多くは、すべてのコード行を最適化する必要があるものと考えて行動します。そうではなく、最も頻繁に実行されたり、長い時間がかかっている 10% のコードを最適化するために、時間を費やす必要があります。その他の 90% については、理解可能性、保守性、モジュール性、実装の容易性を重視して設計します。
システムの機能外要求やアーキテクチャは、リモート プロシージャ呼び出しを実装するために使用するメカニズムの選択に影響を与えます選択肢の間のトレードオフの概要を以下に示します。
メカニズム | 用途 | コメント |
メッセージング | 企業サーバーへの非同期アクセス | メッセージング ミドルウェアは、キューイング、タイムアウト、リカバリ / 再開状況を処理することで、アプリケーションのプログラミング作業を簡単にします。また、メッセージング ミドルウェアは疑似同期モードで使用することもできます。一般に、メッセージング技術では大きなメッセージ サイズをサポートできます。一部の RPC 方式ではメッセージ サイズに制限があり、大きいメッセージを処理するには追加のプログラミングが必要になります。 |
JDBC/ODBC | データベースの呼び出し | 同じサーバーまたは別のサーバーにあるデータベースへの呼び出しを行うために、Java サーブレット用またはアプリケーション プログラム用の、データベースに依存しないインターフェイスがあります。 |
ネイティブ インターフェイス | データベースの呼び出し | 多くのデータベース ベンダーは、ネイティブなアプリケーション プログラム インターフェイスを独自のデータベースに実装し、アプリケーションの移植性を犠牲にして、ODBC より優れた性能を提供しています。 |
リモート プロシージャ呼び出し | リモート サーバー上のプログラムの呼び出し | この処理を行うアプリケーション ビルダがある場合には、RPC レベルでのプログラムは不要です。 |
対話的 | e-ビジネス アプリケーションではあまり使用されない | 一般に、APPC や Socket などのプロトコルを使用した低レベルのプログラム間通信です。 |
多くのシステムでは並行動作と分散コンポーネントが必要です。ほとんどのプログラミング言語は、このような問題のどれについてもほとんど助けてくれません。これまで、アプリケーションでの並行性のニーズと、それをソフトウェアで実装するオプションの両方を理解するための、よい抽象概念が必要なことを見てきました。また、逆説的になりますが、並行ソフトウェアは本質的に非並行ソフトウェアよりも複雑になる一方で、実世界の並行性に対処する必要があるシステムの設計を、大幅に単純化することが可能なことも見てきました。
Rational Unified Process
|