スレッド モデルを選択する
ウィザードを使ってオブジェクトを作成するときには、オブジェクトでサポートするスレッド モデルを選択します。 COM オブジェクトにスレッド サポートを追加すると、複数のクライアントが同時にアプリケーションにアクセスできるため、パフォーマンスが向上します。
指定可能なスレッド モデルを以下の表に示します。
COM オブジェクトのスレッド モデル:
[スレッド モデル] | 説明 | 実装の長所と短所 |
---|---|---|
Single |
サーバーはスレッドをサポートしません。 アプリケーションが一度に 1 つの要求を受け取るよう、COM がクライアント要求をシリアル化します。 |
クライアントは一度に 1 つずつ処理されるため、スレッド サポートは必要ありません。 パフォーマンス上の利点はありません。 |
[アパートメント スレッド モデル](単一スレッド アパートメント) |
オブジェクトを呼び出せるクライアント スレッドが一度に 1 つだけであることが COM によって保証されます。 クライアント呼び出しはすべて、オブジェクトが作成されたスレッドを使って処理されます。 |
オブジェクトが自分のインスタンス データにアクセスするのは安全ですが、グローバル データはクリティカル セクションなどでシリアル化を行って保護する必要があります。 複数の呼び出しをまたがってスレッドのローカル変数を使用することができます。 いくらかパフォーマンス上の利点があります。 |
[使用可能](フリー スレッド モデル。マルチスレッド アパートメントとも呼ばれます) |
オブジェクトは一度に任意の数のスレッドで呼び出しを受け取ることができます。 |
オブジェクトは、クリティカル セクションなどでシリアル化を行って、すべてのインスタンス データおよびグローバル データを保護する必要があります。 複数の呼び出しをまたがってスレッドのローカル変数を使用すると問題が生じる可能性があります。 |
[フリー/アパートメント両用] |
これは、[使用可能]スレッド モデルと同じですが、外へ向かう呼び出し(コールバックなど)が同じスレッド内で実行されると保証されている点が異なります。 |
最大のパフォーマンスと柔軟性が得られます。 外へ向かう呼び出しに渡すパラメータについてアプリケーションでスレッドをサポートする必要はありません。 |
[ニュートラル] |
複数のクライアントから異なるスレッドでオブジェクトを同時に呼び出すことができますが、2 つの呼び出しに間で衝突がないよう COM によって保証されます。 |
複数のメソッドからアクセスされるグローバル データやインスタンス データに関するスレッドの衝突は、開発者が防止する必要があります。ユーザー インターフェイス(ビジュアル コントロール)を持つオブジェクトでこのモデルを使用してはなりません。 このモデルは COM+ でのみ使用できます。 COM の場合は、[アパートメント スレッド モデル]にマップされます。 |
メモ: ローカル変数(コールバック内のものを除く)は、どのスレッド モデルを使った場合でも常に安全です。 これは、ローカル変数はスタック上に格納され、スレッドはそれぞれ独自のスタックを持つためです。 フリー スレッド モデルを使用する場合、コールバック内のローカル変数は安全でない可能性があります。
ウィザードで選択したスレッド モデルによって、オブジェクトがシステム レジストリにどう登録されるかが決まります。 選択したスレッド モデルに従ったオブジェクト実装を行う必要があります。 スレッドセーフなコードの書き方についての一般情報は、「マルチスレッド アプリケーションの作成」を参照してください。
インプロセス サーバーの場合、ウィザードでスレッド モデルを設定すると、CLSID レジストリ エントリのスレッド モデル キーが設定されます。
アウトオブプロセス サーバーは EXE として登録され、COM は Delphi によって、必要である最も高いスレッド モデル用に初期化されます。 たとえば EXE にフリー スレッド オブジェクトが含まれている場合には、フリー スレッド用に初期化されます。つまり、EXE に含まれるすべてのフリー スレッド オブジェクトおよびアパートメント スレッド オブジェクトに対して期待どおりのサポートを提供することができます。 EXE におけるスレッドの動作を上書きするには、ComObj.CoInitFlags 変数を使用します。
目次
フリー スレッド モデルをサポートするオブジェクトの作成
オブジェクトに対して複数のスレッドからアクセスする必要がある場合には必ず、アパートメント スレッド モデルではなくフリー スレッド モデル(または両方)を使用します。 よく見かける例は、クライアント アプリケーションがリモート マシン上のオブジェクトに接続する場合です。 リモート クライアントがオブジェクトのメソッドを呼び出すと、サーバーはサーバー マシンのスレッド プールの 1 つのスレッドでその呼び出しを受信します。 この受信スレッドはローカルで実際のオブジェクトを呼び出します。オブジェクトはフリー スレッド モデルをサポートしているため、スレッドはオブジェクトに対する直接呼び出しを行うことができます。
仮にオブジェクトがフリー スレッド モデルではなくアパートメント スレッド モデルをサポートしているとすると、オブジェクトが作成されたスレッドに呼び出しを転送し、結果を受信スレッドに転送してからクライアントに返さなければなりません。 この方法では余分なマーシャリングが必要になります。
フリー スレッド モデルをサポートするには、それぞれのメソッドについてどうすればインスタンス データにアクセスできるかを考慮する必要があります。 メソッドでインスタンス データに書き込みを行っている場合には、クリティカル セクションなどでシリアル化を行ってインスタンス データを保護する必要があります。 たいてい、クリティカルな呼び出しをシリアル化するオーバーヘッドは、COM のマーシャリング コードを実行するオーバーヘッドよりも小さくなります。
インスタンス データが読み取り専用であれば、シリアル化は必要ありません。
インプロセス サーバーでフリー スレッド モデルを使用すると、フリー スレッド マーシャラとの集約における外部オブジェクトとして動作するため、パフォーマンスを向上することができます。 フリー スレッド マーシャラは、フリー スレッド モデルではないホスト(クライアント)がフリー スレッド モデルの DLL を呼び出したときには、COM の標準スレッド処理のショートカットとなります。
フリー スレッド マーシャラとの集約を作成するには、次の処理が必要です。
- 次のように CoCreateFreeThreadedMarshaler を呼び出して、作成されるフリー スレッド マーシャラが使用できるようオブジェクトの IUnknown インターフェイスを渡します。 CoCreateFreeThreadedMarshaler(self as IUnknown, FMarshaler); CoCreateFreeThreadedMarshaler(static_cast<IUnknown *>(this), &FMarshaler);. この行では、フリー スレッド マーシャラのインターフェイスをクラス メンバ FMarshaler に割り当てています。
- タイプ ライブラリ エディタを使って、CoClass が実装するインターフェイス群に IMarshal インターフェイスを追加します。
- オブジェクトの QueryInterface メソッドで、IDD_IMarshal に対する呼び出しをフリー スレッド マーシャラ(上で FMarshaler として格納したもの)に委譲します。
警告: フリー スレッド マーシャラは、COM マーシャリングによって効率が向上するという通則に当てはまりません。 そのため、使用時には注意が必要です。 特に、インプロセス サーバーではフリー スレッド オブジェクトとしか集約を作成してはならず、(別のスレッドではなく)それを使用するオブジェクトによってしかインスタンス化してはなりません。
アパートメント スレッド モデルをサポートするオブジェクトの作成
(単一スレッドの)アパートメント スレッド モデルを実装するときには、いくつかの規則を守る必要があります。
- アプリケーション内で最初に作成されるスレッドが COM のメイン スレッドになります。 これは通常、WinMain が呼び出されるスレッドです。 これは、COM を初期化解除する最後のスレッドでもなければなりません。
- アパートメント スレッド モデルのスレッドごとにメッセージ ループが必要であり、メッセージ キューを頻繁に確認しなければなりません。
- スレッドが取得した COM インターフェイスのポインタは、そのスレッド内でしか使用できません。
単一スレッド アパートメント モデルは、スレッドをサポートしない場合と、フリー スレッド モデルで完全にマルチスレッドをサポートする場合との中間に位置します。 アパートメント モデルを採用しているサーバーでは、サーバーのすべてのグローバル データ(オブジェクト カウントなど)に対するアクセスがシリアル化されることが約束されています。 これは、さまざまなオブジェクトがさまざまなスレッドからグローバル データにアクセスしようとする可能性があるためです。 ただし、オブジェクトのインスタンス データの場合は、メソッドが常に同じスレッドで呼び出されるため、安全です。
通常、Web ブラウザで使われるコントロールではアパートメント スレッド モデルを使用します。ブラウザ アプリケーションではスレッドが必ずアパートメントとして初期化されるためです。
ニュートラル スレッド モデルをサポートするオブジェクトの作成
COM+ では、フリー スレッドとアパートメント スレッドの中間に位置するニュートラル モデルというスレッド モデルも使用することができます。 このモデルでは、フリー スレッド モデルの場合と同様に、複数のスレッドが同時にオブジェクトにアクセスできます。 オブジェクトが作成されたスレッドに転送するための余分なマーシャリングは行われません。 ただし、競合する呼び出しをオブジェクトが受信しないことは保証されます。
ニュートラル スレッド モデルを使用するオブジェクトを作成するときの規則はアパートメント スレッド オブジェクトを作成する場合とほとんど同じですが、オブジェクトのインターフェイスのさまざまなメソッドからインスタンス データにアクセスできる場合にスレッドの衝突を開発者が防止する必要がない点が異なります。 1 つのインターフェイス メソッドからしかアクセスされないインスタンス データは、自動的にスレッドセーフになります。