いくつかのオペレーティング・システムに共通する特徴は、1 つのプロセスで実行プログラムの複数のスレッドを実行できることです。これにより、アプリケーションが非同期の事象を処理することができ、ポーリング機能がなくても容易に事象駆動アプリケーションを作成できます。この節では、データベース・マネージャーが複数のスレッドを処理する方法を解説し、留意すべき設計の指針を示します。ご使用のプラットフォームがマルチスレッド化機能をサポートしているかどうかを判別するには、 アプリケーション構築の手引き を参照してください。
この節は、マルチスレッドのアプリケーション開発に関する用語 (クリティカル・セクションおよびセマフォーなど) に精通されている方を対象としています。これらの用語について詳しくない方は、ご使用のオペレーティング・システムのプログラミングに関する資料を調べてください。
DB2 アプリケーションは、 文脈 を使用して複数のスレッドから SQL ステートメントを実行することができます。文脈とは、アプリケーションがすべての SQL ステートメントおよび API 呼び出しを実行する環境のことです。すべての接続、作業単位、および他のデータベース資源は、特定の文脈に関連付けられています。各文脈は、アプリケーション内の 1 つまたは複数のスレッドに関連付けられています。
各実行可能 SQL ステートメントでは、最初の実行時サービス呼び出しは常にラッチを取得しようとします。成功すると処理を続行しますが、 (他のスレッドの SQL ステートメントがすでにラッチを取得しているために) 失敗すると、呼び出しは信号セマフォーでこれがポストされるまでブロックされ、それからラッチを取得し処理を続行します。ラッチは SQL ステートメントが処理を終了するまで保持され、その SQL ステートメントに対して生成された最後の実行時サービス呼び出しにより解放されます。
最終的な結果として、他のスレッドが SQL ステートメントを同時に実行しようとしても各 SQL ステートメントはアトミック単位で実行されます。これにより内部データ構造は、異なるスレッドによって同時に変更されることがなくなります。 API も実行時サービスを使用したラッチを使用します。したがって、API には、各文脈内の実行時サービス・ルーチンと同じ制限が課されます。
省略時設定では、すべてのアプリケーションに、すべてのデータベース・アクセスで使用する単一の文脈があります。単一スレッドのアプリケーションではこれで十分ですが、 SQL ステートメントを逐次化すると、単一文脈はマルチスレッド・アプリケーションには不適当になります。次の DB2 API を使用すれば、アプリケーションは各スレッドに別個の文脈を接続して、スレッド間で文脈を渡すことができるようになります。
文脈はプロセス内のスレッド間で交換できますが、プロセス間では交換できません。複数の文脈の使用方法の 1 つは、並行トランザクションのサポートです。上記の文脈の API の使用方法の詳細は、 管理 API 解説書 および 並行トランザクションを参照してください。
マルチスレッドのアプリケーションからデータベースをアクセスする際には、これらの指針に従ってください。
アプリケーションは、SQL ステートメントまたはデータベース・マネージャー・ルーチンが、あるスレッドで処理されている間に、 SQL ステートメントおよびデータベース・マネージャー・ルーチンが使用するユーザー定義のデータ構造が、別のスレッドによって変更されていないことを確認する必要があります。たとえば、他のスレッドの SQL ステートメントが SQLDA を使用している場合は、スレッドが SQLDA を再び割り振ることができないようにしてください。
繰り返しを避けるため、各スレッドにそれぞれのユーザー定義のデータを渡す方が容易であるといえます。これは特に、SQLCA の場合にそういえます。 SQLCA は個々の実行可能 SQL ステートメントばかりでなく、すべてのデータベース・マネージャー・ルーチンによって使用されるからです。 SQLCA に関連したこの問題を避けるための代替手段が 3 つあります。
AIX、Solaris、HP-UX、および Silicon Graphics IRIX では、データベース接続で使用されるコード・ページおよび国別コードを実行時に照会するために使用される関数に対して変更が加えられました。これらは現在ではスレッド・セーフですが、多数の並行データベース接続を使用するマルチスレッド・アプリケーションでは、ロック競合 (およびその結果、パフォーマンスの低下) が起きる可能性があります。
マルチスレッド・アプリケーションでのロック競合を減らすために、新しい環境変数 (DB2_FORCE_NLS_CACHE) が作成されました。 DB2_FORCE_NLS_CACHE が TRUE に設定されると、コード・ページおよび国別コード情報は、スレッドが最初にアクセスする際に保管されます。その時点から、キャッシュされた情報は、この情報を要求する他のすべてのスレッドで使用されます。この情報を保管するとロック競合は削減され、状況によってはパフォーマンスが向上します。
接続間のロケール設定をアプリケーションが変更する場合には、 DB2_FORCE_NLS_CACHE を TRUE に設定するべきではありません。そのように設定すると、ロケール設定が変更されても、元のロケール情報が戻されます。一般的には、マルチスレッド・アプリケーションはロケール設定を変更しません。これにより、アプリケーションはスレッド・セーフのままであることができます。
複数のスレッドを使用するアプリケーションは、当然のことながら、単一スレッドを使用するアプリケーションよりも複雑です。この余分の複雑さにより、予期しない問題がいくつか生じる可能性が潜んでいます。マルチスレッドのアプリケーションを作成するときには、次の事柄に注意を払ってください。
アプリケーション内の各文脈には、データベース・オブジェクトに対するロックなど、それぞれ固有のデータベース資源があります。 このため、2 つの文脈が同じデータベース・オブジェクトにアクセスしている場合、デッドロックを引き起こす可能性があります。データベース・マネージャーはデッドロックを検出し、一方の文脈が SQLCODE -911 を受け取ると、その作業単位がロールバックされます。
文脈間の従属関係を確立するプログラミング技法に注意してください。ラッチ、セマフォー、およびクリティカル・セクションは、そのような従属関係を確立するプログラミング技法の例です。アプリケーションに 2 つの文脈があり、その文脈間にはアプリケーションの従属関係とデータベースの従属関係のどちらもが存在する場合、アプリケーションがデッドロックする可能性があります。従属関係のあるものがデータベース・マネージャーの管理範囲外にある場合、デッドロックが検出されないので、アプリケーションは中断またはハングします。
この種の問題の例として、2 つの文脈があり、そのどちらも共通のデータ構造にアクセスするアプリケーションを考えてみましょう。両方の文脈がそのデータ構造を同時に変更することを避けるため、データ構造はセマフォーによって保護されます。文脈は次のようになります。
context 1 SELECT * FROM TAB1 FOR UPDATE.... UPDATE TAB1 SET.... get semaphore access data structure release semaphore COMMIT context 2 get semaphore access data structure SELECT * FROM TAB1... release semaphore COMMIT
最初の文脈が SELECT および UPDATE ステートメントを正常に実行しているときに、 2 番目の文脈がセマフォーを獲得してデータ構造にアクセスするとします。最初の文脈がセマフォーを獲得しようとしますが、2 番目の文脈がセマフォーを保持しているため、獲得できません。ここで 2 番目の文脈は表 TAB1 から行を読み取ろうとしますが、最初の文脈が保持するデータベース・ロックによってその操作が停止してしまいます。アプリケーションは、文脈 1 が文脈 2 の前に完了できず、文脈 2 が文脈 1 の完了を待っている状態になります。アプリケーションはデッドロックしますが、データベース・マネージャーはセマフォーの従属関係を知らないため、文脈はロールバックされません。そのため、アプリケーションは延期状態になってしまいます。
データベース・マネージャーはスレッド間のデッドロックを検出できないため、デッドロックしないように (または少なくともそれを回避できるように) アプリケーションを設計してコーディングしなければなりません。上の例では、いくつかの方法でデッドロックを回避することができます。
文脈 1 のコードを変更して、セマフォーを獲得する前にコミットを実行するようにします。
文脈 2 のコードを変更して、SELECT を実行する前にセマフォーを解除するようにします。
文脈 1 のコードを変更して、SELECT ステートメントを実行する前にセマフォーを獲得するようにします。この技法は、動作はしますが、あまりお勧めできません。というのは、セマフォーはデータベース・マネージャーへのアクセスを逐次化するため、複数のスレッドを使用する効果が発揮されないからです。
これではデッドロックを防ぐことはできませんが、実行を再開することはできます。 文脈 2 は、要求されたロックを獲得できないため、結局はロールバックされます。 ロールバックのエラーを処理するときには、文脈 2 はセマフォーを解除しなければなりません。セマフォーを解除すると、文脈 1 が継続でき、文脈 2 が解放されて作動を再試行します。
デッドロックを回避する技法を、前述の例に当てはめて示しましたが、この方法はすべてのマルチスレッド・アプリケーションに適用することができます。一般に、保護資源を扱うようにデータベース・マネージャーを扱うならば、マルチスレッド・アプリケーションで問題が生じることはありません。