マルチスレッドアパートメント
マルチスレッド アパートメント モデルでは、フリースレッドとして初期化されたプロセス内のすべてのスレッドが 1 つのアパートメントに存在します。 そのため、スレッド間でマーシャリングする必要はありません。 COM はこのモデルでウィンドウ メッセージを使用しないため、スレッドはメッセージを取得してディスパッチする必要はありません。
マルチスレッド アパートメント内のオブジェクトのメソッドの呼び出しは、アパートメント内の任意のスレッドで実行できます。 呼び出しのシリアル化はありません。多くの呼び出しは、同じメソッドまたは同じオブジェクトに対して同時に発生する可能性があります。 マルチスレッド アパートメントで作成されたオブジェクトは、他のスレッドからのメソッドの呼び出しをいつでも処理できる必要があります。
オブジェクトへの呼び出しはいかなる方法でもシリアル化されないため、マルチスレッド オブジェクトの同時実行により最高のパフォーマンスが提供され、クロススレッド、クロスプロセス、およびクロスマシンの呼び出しに対してマルチプロセッサ ハードウェアを最大限に活用できます。 ただし、これは、オブジェクトのコードが、通常、このセクションで後述するイベント オブジェクト、クリティカル セクション、ミューテックス、セマフォなどの同期プリミティブを使用して、インターフェイス実装で同期を提供する必要があることを意味します。 さらに、オブジェクトはアクセスしているスレッドの有効期間を制御しないため、スレッド固有の状態がオブジェクト (スレッド ローカル ストレージ) に格納されることはありません。
マルチスレッド アパートメントの同期に関する重要な考慮事項を次に示します。
- COM では、シングルスレッド アパートメントに対してのみ呼び出し同期が提供されます。
- マルチスレッド アパートメントは、(同じスレッドで) 呼び出しを行っている間は呼び出しを受け取りません。
- マルチスレッド アパートメントは、入力同期呼び出しを行うことはできません。
- 非同期呼び出しは、マルチスレッド アパートメントの同期呼び出しに変換されます。
- メッセージ フィルターは、マルチスレッド アパートメント内のどのスレッドにも呼び出されません。
スレッドをフリー スレッドとして初期化するには、COINIT_MULTITHREADED を指定して CoInitializeEx を呼び出します。 インプロセス サーバー スレッドの詳細については、「インプロセス サーバー スレッドの問題」を参照してください。
複数のクライアントは、フリー スレッドをサポートするオブジェクトを異なるスレッドから同時に呼び出すことができます。 フリー スレッドのアウトプロセス サーバーでは、COM は RPC サブシステムを通じてサーバー プロセス内にスレッドのプールを作成し、クライアント呼び出し (または複数のクライアント呼び出し) をこれらのスレッドのいずれかによっていつでも配信できます。 アウトプロセス サーバーも、そのクラス ファクトリに同期を実装する必要があります。 フリー スレッドのインプロセス オブジェクトは、クライアントの複数のスレッドから直接呼び出しを受け取ることができます。
クライアントは、複数のスレッドで COM 作業を実行できます。 すべてのスレッドは、同じマルチスレッド アパートメントに属します。 インターフェイス ポインターは、マルチスレッド アパートメント内のスレッドからスレッドに直接渡されるため、インターフェイス ポインターはスレッド間でマーシャリングされません。 メッセージ フィルター (IMessageFilter の実装) は、マルチスレッド アパートメントでは使用されません。 クライアント スレッドは、アパートメント外のオブジェクトに対して COM 呼び出しを行うと中断され、呼び出しが戻ったときに再開されます。 プロセス間の呼び出しは、RPC によって引き続き処理されます。
フリー スレッド モデルで初期化されたスレッドは、独自の同期を実装する必要があります。 このセクションで前述したように、Windows では次の同期プリミティブを通じてこの実装が可能になります。
- イベント オブジェクトは、イベントが発生したことを 1 つ以上のスレッドに通知する方法を提供します。 プロセス内の任意のスレッドでイベント オブジェクトを作成できます。 イベントのハンドルは、イベント作成関数 CreateEvent によって返されます。 イベント オブジェクトが作成されると、オブジェクトへのハンドルを持つスレッドは、実行を続行する前にイベント オブジェクトを待機できます。
- クリティカル セクションは、実行前に共有データのセットへの排他的アクセスを必要とし、単一プロセス内のスレッドによってのみ使用されるコードのセクションに使用されます。 クリティカル セクションは、一度に 1 つのスレッドしか通過し得るターンタイルのようなもので、次のように動作します。
- 一度に複数のスレッドが共有データにアクセスしないようにするために、プロセスのプライマリ スレッドはグローバル CRITICAL_SECTION データ構造を割り当て、そのメンバーを初期化します。 クリティカル セクションに入るスレッドは、EnterCriticalSection 関数を呼び出し、データ構造のメンバーを変更します。
- クリティカル セクションを入力しようとしているスレッドが EnterCriticalSection を呼び出し、チェックして、CRITICAL_Standard Edition CTION データ構造が変更されたかどうかを確認します。 その場合、別のスレッドが現在クリティカル セクションにあり、後続のスレッドがスリープ状態になります。 クリティカル セクションを離れるスレッドは LeaveCriticalSection を呼び出し、データ構造をリセットします。 スレッドがクリティカル セクションを離れると、システムはスリープ状態のスレッドの 1 つを起動し、そのスレッドがクリティカル セクションに入ります。
- ミューテックスは、別のプロセスで実行されているスレッドからアクセスできることを除いて、クリティカル セクションと同じ機能を実行します。 ミューテックス オブジェクトを所有することは、議論の中でフロアを持つことに似ています。 プロセスは、ハンドルを返す CreateMutex 関数を呼び出してミューテックス オブジェクトを作成します。 ミューテックス オブジェクトを要求した最初のスレッドが、そのオブジェクトの所有権を取得します。 スレッドがミューテックスの使用を終了すると、所有権は先着順で他のスレッドに渡されます。
- セマフォは、利用可能なリソースの参照カウントを維持するために使用されます。 スレッドは、CreateSemaphore 関数を呼び出し、リソースへのポインター、初期リソース数、および最大リソース数を渡すことによって、リソースのセマフォを作成します。 この関数はハンドルを返します。 リソースを要求するスレッドは、WaitForSingleObject 関数の呼び出しでセマフォ ハンドルを渡します。 セマフォ オブジェクトはリソースをポーリングして、使用可能かどうかを判断します。 使用可能な場合、セマフォはリソース数をデクリメントし、待機中のスレッドをウェイクアップします。 カウントが 0 の場合、別のスレッドがリソースを解放してセマフォがカウントを 1 にインクリメントするまで、スレッドはスリープ状態のままになります。
関連トピック