Beschreibungen und Funktionsweisen von OLE-Threadingmodellen

In diesem Artikel werden OLE-Threadingmodelle beschrieben.

Ursprüngliche Produktversion: OLE-Threadingmodelle
Ursprüngliche KB-Nummer: 150777

Zusammenfassung

COM-Objekte können in mehreren Threads eines Prozesses verwendet werden. Die Begriffe "Singlethreaded Apartment" (STA) und "Multithreaded Apartment" (MTA) werden verwendet, um ein konzeptionelles Framework zum Beschreiben der Beziehung zwischen Objekten und Threads, der Parallelitätsbeziehungen zwischen Objekten, der Mittel, mit denen Methodenaufrufe an ein Objekt übermittelt werden, und die Regeln zum Übergeben von Schnittstellenzeigern zwischen Threads zu erstellen. Komponenten und ihre Clients wählen zwischen den folgenden beiden Apartmentmodellen, die derzeit von COM unterstützt werden:

  1. Singlethreaded Apartment Model (STA): Mindestens ein Thread in einem Prozess verwendet COM, und Aufrufe von COM-Objekten werden von COM synchronisiert. Schnittstellen werden zwischen Threads gemarshallt. Ein degenerierter Fall des Singlethread-Apartmentmodells, bei dem nur ein Thread in einem bestimmten Prozess COM verwendet, wird als Singlethreading-Modell bezeichnet. Früher wurde das STA-Modell manchmal einfach als "Apartmentmodell" bezeichnet.

  2. Multithread-Apartmentmodell (MTA): Mindestens ein Thread verwendet COM, und Aufrufe von COM-Objekten, die dem MTA zugeordnet sind, werden direkt von allen Threads ausgeführt, die dem MTA zugeordnet sind, ohne dass Systemcode zwischen Aufrufer und Objekt interpositioniert wird. Da mehrere gleichzeitige Clients Objekte mehr oder weniger gleichzeitig (gleichzeitig auf Mehrprozessorsystemen) aufrufen können, müssen Objekte ihren internen Zustand selbst synchronisieren. Schnittstellen werden nicht zwischen Threads gemarshallt. In früheren Versionen wurde dieses Modell manchmal als "Freethread-Modell" bezeichnet.

  3. Sowohl das STA-Modell als auch das MTA-Modell können im gleichen Prozess verwendet werden. Dies wird manchmal als "mixed-model"-Prozess bezeichnet.

Der MTA wird in NT 4.0 eingeführt und ist in Windows 95 mit DCOM95 verfügbar. Das STA-Modell ist in Windows NT 3.51 und Windows 95 sowie NT 4.0 und Windows 95 mit DCOM95 vorhanden.

Übersicht

Die Threadingmodelle in COM stellen den Mechanismus für Komponenten bereit, die verschiedene Threadingarchitekturen verwenden, um zusammenzuarbeiten. Sie stellen auch Synchronisierungsdienste für Komponenten bereit, die sie benötigen. Beispielsweise kann ein bestimmtes Objekt so konzipiert sein, dass es nur von einem einzelnen Thread aufgerufen wird und nicht gleichzeitige Aufrufe von Clients synchronisiert. Wenn ein solches Objekt gleichzeitig von mehreren Threads aufgerufen wird, stürzt es ab oder verursacht Fehler. COM stellt die Mechanismen für den Umgang mit dieser Interoperabilität von Threadingarchitekturen bereit.

Selbst threadfähige Komponenten benötigen häufig Synchronisierungsdienste. Beispielsweise erfordern Komponenten mit einer grafischen Benutzeroberfläche (GUI), z. B. OLE/ActiveX-Steuerelemente, direkte aktive Einbettungen und ActiveX-Dokumente, die Synchronisierung und Serialisierung von COM-Aufrufen und Fenstermeldungen. COM stellt diese Synchronisierungsdienste bereit, sodass diese Komponenten ohne komplexen Synchronisierungscode geschrieben werden können.

Eine "Wohnung" hat mehrere miteinander verbundene Aspekte. Erstens ist es ein logisches Konstrukt, um über Parallelität nachzudenken, z. B. wie Threads sich auf eine Gruppe von COM-Objekten beziehen. Zweitens ist es eine Reihe von Regeln, die Programmierer befolgen müssen, um das Parallelitätsverhalten zu erhalten, das sie von der COM-Umgebung erwarten. Schließlich ist es vom System bereitgestellter Code, der Programmierern hilft, threadparallelität in Bezug auf COM-Objekte zu verwalten.

Der Begriff "Wohnung" stammt aus einer Metapher, in der ein Prozess als diskrete Entität verstanden wird, z. B. ein "Gebäude", das in eine Reihe verwandter, aber verschiedener "Gebietsschemas" unterteilt ist, die als "Wohnungen" bezeichnet werden. Ein Apartment ist ein "logischer Container", der eine Zuordnung zwischen Objekten und in einigen Fällen Threads erstellt. Threads sind keine Apartments, obwohl es möglicherweise einen einzelnen Thread gibt, der einem Apartment im STA-Modell logisch zugeordnet ist. Objekte sind keine Apartments, obwohl jedes Objekt nur einem Apartment zugeordnet ist. Wohnungen sind jedoch mehr als nur ein logisches Konstrukt; ihre Regeln beschreiben das Verhalten des COM-Systems. Wenn die Regeln der Apartmentmodelle nicht eingehalten werden, funktionieren COM-Objekte nicht ordnungsgemäß.

Weitere Informationen

Ein Singlethread-Apartment (STA) ist ein Satz von COM-Objekten, die einem bestimmten Thread zugeordnet sind. Diese Objekte werden dem Apartment zugeordnet, indem sie vom Thread erstellt werden oder, genauer gesagt, zuerst für das COM-System (in der Regel durch Marshalling) für den Thread verfügbar gemacht werden. EIN STA wird als Ort angesehen, an dem ein Objekt oder ein Proxy "lebt". Wenn von einem anderen Apartment (im selben oder einem anderen Prozess) auf das Objekt oder den Proxy zugegriffen werden muss, muss der Schnittstellenzeiger an das Apartment gemarshallt werden, in dem ein neuer Proxy erstellt wird. Wenn die Regeln des Apartmentmodells befolgt werden, sind keine direkten Aufrufe von anderen Threads im gleichen Prozess für dieses Objekt zulässig. Dies würde gegen die Regel verstoßen, dass alle Objekte in einem bestimmten Apartment in einem einzelnen Thread ausgeführt werden. Die Regel ist vorhanden, da der meiste Code, der in einem STA ausgeführt wird, nicht ordnungsgemäß funktioniert, wenn er in zusätzlichen Threads ausgeführt wird.

Der thread, der einem STA zugeordnet ist, muss oder CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) aufrufen CoInitialize und fenstermeldungen abrufen und senden, damit die zugeordneten Objekte eingehende Anrufe empfangen können. COM verteilt und synchronisiert Aufrufe von Objekten in einem STA mithilfe von Fenstermeldungen, wie weiter unten in diesem Artikel beschrieben.

Der "Standard STA" ist der Thread, der oder CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) zuerst innerhalb eines bestimmten Prozesses aufruftCoInitialize. Die Standard STA eines Prozesses muss aktiv bleiben, bis alle COM-Arbeiten abgeschlossen sind, da einige proc-Objekte immer in die Standard STA geladen werden, wie weiter unten in diesem Artikel beschrieben.

Windows NT 4.0 und DCOM95 führen einen neuen Apartmenttyp namens Multithread-Apartment (MTA) ein. Ein MTA ist ein Satz von COM-Objekten, die einem Satz von Threads im Prozess zugeordnet sind, sodass jeder Thread jede Objektimplementierung direkt ohne Die Interposition von Systemcode aufrufen kann. Schnittstellenzeiger auf jedes Objekt im MTA können zwischen den Threads übergeben werden, die dem MTA zugeordnet sind, ohne gemarshallt werden zu müssen. Alle Threads im Prozess, die aufrufen CoInitializeEx(NULL, COINIT_MULTITHREADED) , sind dem MTA zugeordnet. Im Gegensatz zur oben beschriebenen STA müssen die Threads in einem MTA keine Fensternachrichten für die zugeordneten Objekte abrufen und verteilen, um eingehende Anrufe zu empfangen. COM synchronisiert keine Aufrufe von Objekten in einem MTA. Objekte in einem MTA müssen ihren internen Zustand durch die Interaktion mehrerer gleichzeitiger Threads vor Beschädigungen schützen, und sie können keine Annahmen darüber treffen, dass der Inhalt von Thread-Local Storage zwischen verschiedenen Methodenaufrufen konstant bleibt.

Ein Prozess kann über eine beliebige Anzahl von STAs verfügen, höchstens aber über einen MTA. Der MTA besteht aus einem oder mehreren Threads. STAs verfügen jeweils über einen Thread. Ein Thread gehört höchstens zu einer Wohnung. Objekte gehören nur zu einem Apartment. Schnittstellenzeiger sollten immer zwischen Wohnungen gemarshallt werden (obwohl das Ergebnis des Marshallings ein direkter Zeiger und kein Proxy sein kann). Weitere Informationen finden Sie unter CoCreateFreeThreadedMarshaler.

Ein Prozess wählt eines der von COM bereitgestellten Threadingmodelle aus. Ein STA-Modellprozess verfügt über mindestens einen STAs und keinen MTA. Ein MTA-Modellprozess verfügt über einen MTA mit mindestens einem Thread und keine STAs. Ein Prozess mit gemischten Modellen verfügt über einen MTA und eine beliebige Anzahl von STAs.

Singlethread-Apartmentmodell

Der Thread eines STA muss oder CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) aufrufen CoInitialize und Fenstermeldungen abrufen und verteilen, da COM Fenstermeldungen verwendet, um Aufrufe an ein Objekt in diesem Modell zu synchronisieren und zu senden. Weitere Informationen finden Sie weiter unten im Abschnitt REFERENCES.

Server, der das STA-Modell unterstützt:

Im STA-Modell werden Aufrufe an ein Objekt von COM auf die gleiche Weise synchronisiert wie Fenstermeldungen, die an ein Fenster gesendet werden. Aufrufe werden mithilfe von Fenstermeldungen an den Thread übermittelt, der das Objekt erstellt hat. Folglich muss der Thread des Objekts und DispatchMessage aufrufenGet/PeekMessage, um Aufrufe zu empfangen. COM erstellt ein ausgeblendetes Fenster, das jedem STA zugeordnet ist. Ein Aufruf eines Objekts von außerhalb des STA wird von der COM-Runtime mithilfe einer Fenstermeldung, die an dieses ausgeblendete Fenster gesendet wird, an den Thread des Objekts übertragen. Wenn der thread, der dem STA des Objekts zugeordnet ist, die Nachricht abruft und sendet, empfängt die Fensterprozedur für das ausgeblendete Fenster, das ebenfalls von COM implementiert wird. Die Fensterprozedur wird von der COM-Runtime verwendet, um den thread zu "verbinden", der dem STA zugeordnet ist, da sich die COM-Runtime auf beiden Seiten des Aufrufs vom COM-eigenen Thread an den STA-Thread befindet. Die COM-Runtime (die jetzt im STA-Thread ausgeführt wird) ruft "up" über einen von COM bereitgestellten Stub in die entsprechende Schnittstellenmethode des Objekts auf. Der ausführungspfad, der vom Methodenaufruf zurückgegeben wird, kehrt den "up"-Aufruf um. Der Aufruf kehrt an den Stub und die COM-Runtime zurück, die die Steuerung über eine Fenstermeldung an den COM-Laufzeitthread zurückgibt, der dann über den COM-Kanal an den ursprünglichen Aufrufer zurückgibt.

Wenn mehrere Clients ein STA-Objekt aufrufen, werden die Aufrufe automatisch in die Warteschlange eingereiht, indem der im STA verwendete Kontrollmechanismus übertragen wird. Das -Objekt empfängt jedes Mal einen Aufruf, wenn sein STA Nachrichten abruft und sendet. Da die Aufrufe von COM auf diese Weise synchronisiert werden und die Aufrufe immer über den einzelnen Thread übermittelt werden, der dem STA des Objekts zugeordnet ist, müssen die Schnittstellenimplementierungen des Objekts keine Synchronisierung bereitstellen.

Hinweis

Das Objekt kann erneut eingegeben werden, wenn eine Schnittstellenmethodenimplementierung Während der Verarbeitung eines Methodenaufrufs Nachrichten abruft und verteilt, wodurch ein anderer Aufruf an das Objekt von derselben STA übermittelt wird. Dies geschieht häufig, wenn ein STA-Objekt einen ausgehenden (apartment-/prozessübergreifenden) Aufruf mithilfe von COM vornimmt. Dies ist identisch mit der Art und Weise, wie eine Fensterprozedur erneut eingegeben werden kann, wenn sie Beim Verarbeiten einer Nachricht Nachrichten abruft und verteilt. COM verhindert nicht den erneuten Eintritt in denselben Thread, verhindert jedoch die gleichzeitige Ausführung. Es bietet auch ein Mittel, mit dem die COM-bezogene Wiedereinstiegsmanagierung gesteuert werden kann. Weitere Informationen finden Sie weiter unten im Abschnitt REFERENCES. Das Objekt wird nicht erneut eingegeben, wenn Methodenimplementierungen nicht aus seinem Apartment heraus aufrufen oder anderweitig Nachrichten abrufen und senden.

Kundenverantwortlichkeiten im STA-Modell:

Clientcode, der in einem Prozess und/oder Thread ausgeführt wird, der das STA-Modell verwendet, muss Schnittstellen eines Objekts zwischen Apartments mithilfe CoMarshalInterThreadInterfaceInStream von und CoGetInterfaceAndReleaseStreammarshallen. Wenn Apartment 1 im Client beispielsweise über einen Schnittstellenzeiger verfügt und Apartment 2 dessen Verwendung erfordert, muss Apartment 1 die Schnittstelle mithilfe von CoMarshalInterThreadInterfaceInStreammarshallen. Das von dieser Funktion zurückgegebene Streamobjekt ist threadsicher, und sein Schnittstellenzeiger sollte in einer direkten Speichervariable gespeichert werden, auf die über Apartment 2 zugegriffen werden kann. Apartment 2 muss diese Datenstromschnittstelle an CoGetInterfaceAndReleaseStream übergeben, um die Schnittstelle für das zugrunde liegende Objekt aufzuheben und einen Zeiger auf einen Proxy zurückzugeben, über den auf das Objekt zugegriffen werden kann.

Die Standard Wohnung eines bestimmten Prozesses sollte aktiv bleiben, bis der Client alle COM-Arbeiten abgeschlossen hat, da einige inproc-Objekte in das Standard-Apartment geladen werden. (Weitere Informationen finden Sie unten).

Multithread-Apartmentmodell

Ein MTA ist die Auflistung von Objekten, die von allen Threads im Prozess erstellt oder verfügbar gemacht werden, die aufgerufen CoInitializeEx(NULL, COINIT_MULTITHREADED)haben.

Hinweis

Aktuelle Implementierungen von COM ermöglichen es, dass ein Thread, der COM nicht explizit initialisiert, Teil des MTA ist. Ein Thread, der COM nicht initialisiert, ist nur Dann Teil des MTA, wenn er mit der Verwendung von COM beginnt, nachdem mindestens ein anderer Thread im Prozess zuvor aufgerufen CoInitializeEx(NULL, COINIT_MULTITHREADED)hat. (Es ist sogar möglich, dass COM den MTA selbst initialisiert hat, wenn kein Clientthread dies explizit getan hat, z. B. ein Thread, der einem STA-Aufruf CoGetClassObject/CoCreateInstance[Ex] zugeordnet ist. in einer CLSID, die als "ThreadingModel=Free" gekennzeichnet ist, erstellt COM implizit einen MTA, in den das Klassenobjekt geladen wird.) Informationen zur Interoperabilität des Threadingmodells finden Sie unten.

Dies ist jedoch eine Konfiguration, die unter bestimmten Umständen Probleme verursachen kann, z. B. Zugriffsverletzungen. Daher wird empfohlen, dass jeder Thread, der COM-Arbeit ausführen muss, COM durch Aufrufen CoInitializeEx von initialisieren und dann nach Abschluss der COM-Arbeit aufrufen CoUninitialize. Die Kosten für die "unnötige" Initialisierung eines MTA sind minimal.

MTA-Threads müssen keine Nachrichten abrufen und verteilen, da COM keine Fensternachrichten in diesem Modell verwendet, um Aufrufe an ein Objekt zu übermitteln.

  • Server, der das MTA-Modell unterstützt:

    Im MTA-Modell werden Aufrufe eines Objekts nicht durch COM synchronisiert. Mehrere Clients können gleichzeitig ein Objekt aufrufen, das dieses Modell für verschiedene Threads unterstützt, und das Objekt muss die Synchronisierung in seinen Schnittstellen-/Methodenimplementierungen mithilfe von Synchronisierungsobjekten wie Ereignissen, Mutexen, Semaphoren usw. bereitstellen. MTA-Objekte können gleichzeitige Aufrufe von mehreren Out-of-Process-Clients über einen Pool von COM-erstellten Threads empfangen, die zum Prozess des Objekts gehören. MTA-Objekte können gleichzeitige Aufrufe von mehreren Prozessclients in mehreren Threads empfangen, die dem MTA zugeordnet sind.

  • Kundenverantwortlichkeiten im MTA-Modell:

    Clientcode, der in einem Prozess und/oder Thread ausgeführt wird, der das MTA-Modell verwendet, muss keine Schnittstellenzeiger eines Objekts zwischen sich selbst und anderen MTA-Threads marshallen. Stattdessen kann ein MTA-Thread einen Schnittstellenzeiger aus einem anderen MTA-Thread als direkten Speicherzeiger verwenden. Wenn ein Clientthread einen Aufruf an ein Out-of-Process-Objekt sendet, wird er angehalten, bis der Aufruf abgeschlossen ist. Aufrufe können für Objekte eingehen, die dem MTA zugeordnet sind, während alle mit der Anwendung erstellten Threads, die dem MTA zugeordnet sind, bei ausgehenden Aufrufen blockiert werden. In diesem Fall und im Allgemeinen werden eingehende Aufrufe über Threads übermittelt, die von der COM-Runtime bereitgestellt werden. Nachrichtenfilter (IMessageFilter) sind nicht für die Verwendung im MTA-Modell verfügbar.

Mixed-Threading-Modelle

Ein Prozess, der das Mixed-Threading-Modell unterstützt, verwendet einen MTA und mindestens einen STAs. Schnittstellenzeiger müssen zwischen allen Wohnungen gemarshallt werden, können aber ohne Marshalling innerhalb des MTA verwendet werden. Aufrufe von Objekten in einem STA werden von COM synchronisiert, um nur in einem Thread ausgeführt zu werden, während Aufrufe von Objekten im MTA dies nicht sind. Aufrufe von einem STA an einen MTA durchlaufen jedoch normalerweise vom System bereitgestellten Code und wechseln vom STA-Thread zu einem MTA-Thread, bevor sie an das -Objekt übermittelt werden.

Hinweis

Informationen zu Fällen, in denen direkte Zeiger verwendet werden können, und wie ein STA-Thread direkt ein Objekt aufrufen kann, das zuerst dem MTA und umgekehrt aus mehreren Apartments zugeordnet ist, finden Sie in der SDK-Dokumentation CoCreateFreeThreadedMarshaler() zu und in der Erläuterung dieser API weiter unten.

Auswählen der Threadingmodelle

Eine Komponente kann das STA-Modell, das MTA-Modell oder eine Kombination aus beiden unterstützen, indem sie das Mixed-Threading-Modell verwendet. Beispielsweise kann ein Objekt, das umfangreiche E/A-Vorgänge ausführt, MTA unterstützen, um clients maximale Antworten zu bieten, indem schnittstellenaufrufe während der E/A-Latenz möglich sind. Alternativ entscheidet sich ein Objekt, das mit dem Benutzer interagiert, fast immer für die Unterstützung von STA, um eingehende COM-Aufrufe mit seinen GUI-Vorgängen zu synchronisieren. Die Unterstützung des STA-Modells ist einfacher, da COM die Synchronisierung bereitstellt. Die Unterstützung des MTA-Modells ist schwieriger, da das Objekt die Synchronisierung implementieren muss, aber die Antwort auf Clients ist besser, da die Synchronisierung für kleinere Codeabschnitte verwendet wird und nicht für den gesamten Schnittstellenaufruf, wie von COM bereitgestellt.

Das STA-Modell wird auch von Microsoft Transaction Server (MTS, zuvor mit dem Codenamen "Viper") verwendet, und daher sollten DLL-basierte Objekte, die die Ausführung in der MTS-Umgebung planen, das STA-Modell verwenden. Objekte, die für das MTA-Modell implementiert sind, funktionieren in einer MTS-Umgebung normalerweise einwandfrei. Sie werden jedoch weniger effizient ausgeführt, da sie unnötige Threadsynchronisierungsprimitiven verwenden.

Markieren des unterstützten Threadingmodells von In-Proc Servern

Ein Thread verwendet das MTA-Modell, wenn er COM aufruft CoInitializeEx(NULL, COINIT_MULTITHREADED) oder verwendet, ohne es zu initialisieren. Ein Thread verwendet das STA-Modell, wenn er oder CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)aufruftCoInitialize.

Die CoInitialize APIs bieten die Apartmentsteuerung für Clientcode und für Objekte, die in gepackt sind. EXEs, da der Startcode der COM-Runtime COM auf die gewünschte Weise initialisieren kann.

Ein in proc (DLL-basierter) COM-Server ruft jedoch nicht auf CoInitialize/CoInitializeEx , da diese APIs zum Zeitpunkt des Ladens des DLL-Servers aufgerufen wurden. Daher muss ein DLL-Server die Registrierung verwenden, um COM über das unterstützte Threadingmodell zu informieren, damit COM sicherstellen kann, dass das System auf eine Weise funktioniert, die mit dem System kompatibel ist. Zu diesem Zweck wird ein benannter Wert des CLSID\InprocServer32-Schlüssels ThreadingModel der Komponente wie folgt verwendet:

  • ThreadingModel Wert nicht vorhanden: Unterstützt das Singlethreading-Modell.
  • ThreadingModel=Apartment: Unterstützt das STA-Modell.
  • ThreadingModel=Both: Unterstützt STA- und MTA-Modell.
  • ThreadingModel=Free: Unterstützt nur MTA.

Hinweis

ThreadingModel ist ein benannter Wert, kein Unterschlüssel von InprocServer32, wie in einigen früheren Versionen der Win32-Dokumentation falsch dokumentiert.

Threadingmodelle von In-Proc-Servern werden weiter unten in diesem Artikel erläutert. Wenn ein In-Proc-Server viele Arten von Objekten bereitstellt (jedes mit einer eigenen eindeutigen CLSID), kann jeder Typ einen anderen ThreadingModel Wert haben. Anders ausgedrückt: Das Threadingmodell wird pro CLSID und nicht pro Codepaket/DLL verwendet. Die API-Einstiegspunkte, die zum "Bootstrap" und Abfragen aller in proc-Server (DLLGetClassObject(), ) erforderlich sind, DLLCanUnloadNow()müssen jedoch threadsicher für jeden In-Proc-Server sein, der mehrere Threads unterstützt (d. h. der ThreadingModel Wert Apartment, Both oder Free).

Wie bereits erwähnt, markieren sich Out-of-Process-Server nicht mit dem ThreadingModel-Wert. Stattdessen verwenden CoInitialize sie oder CoInitializeEx. DLL-basierte Server, die eine Out-of-Process-Ausführung mit der "Ersatzfunktion" von COM (z. B. dem vom System bereitgestellten Ersatzzeichen DLLHOST.EXE) erwarten, befolgen die Regeln für DLL-basierte Server. in diesem Fall gibt es keine besonderen Überlegungen.

Wenn der Client und das Objekt verschiedene Threadingmodelle verwenden

Die Interaktion zwischen einem Client und einem Out-of-Process-Objekt ist auch dann unkompliziert, wenn verschiedene Threadingmodelle verwendet werden, da sich client und objekt in unterschiedlichen Prozessen befinden und COM an der Übergabe von Aufrufen vom Client an das Objekt beteiligt ist. Da COM zwischen dem Client und dem Server interposiert wird, stellt es den Code für die Interoperation der Threadingmodelle bereit. Wenn beispielsweise ein STA-Objekt gleichzeitig von mehreren STA- oder MTA-Clients aufgerufen wird, synchronisiert COM die Aufrufe, indem die entsprechenden Fensternachrichten in der Nachrichtenwarteschlange des Servers platziert werden. Die STA des Objekts empfängt bei jedem Abrufen und Senden von Nachrichten einen Aufruf. Alle Kombinationen der Threading-Modellinteroperabilität sind zwischen Clients und Out-of-Process-Objekten zulässig und werden vollständig unterstützt.

Die Interaktion zwischen einem Client und einem in proc-Objekt, das verschiedene Threadingmodelle verwendet, ist komplizierter. Obwohl sich der Server in proc befindet, muss com sich in einigen Fällen zwischen dem Client und dem -Objekt verzahnen. Beispielsweise kann ein proc-Objekt, das das STA-Modell unterstützt, von mehreren Threads eines Clients gleichzeitig aufgerufen werden. COM kann den Clientthreads nicht erlauben, direkt auf die Schnittstelle des Objekts zuzugreifen, da das Objekt nicht für diesen gleichzeitigen Zugriff konzipiert ist. Stattdessen muss COM sicherstellen, dass Aufrufe synchronisiert und nur von dem Thread ausgeführt werden, der dem STA zugeordnet ist, der das Objekt "enthält". Trotz der zusätzlichen Komplexität sind alle Kombinationen der Threadingmodellinteroperabilität zwischen Clients und in-proc-Objekten zulässig.

Threadingmodelle in Out-of-Process-Servern (EXE-basiert)

Im Folgenden finden Sie drei Kategorien von Out-of-Process-Servern, von denen jede von jedem COM-Client unabhängig vom von diesem Client verwendeten Threadingmodell verwendet werden kann:

  1. STA Model Server:

    Der Server funktioniert com in mindestens einem STAs. Eingehende Aufrufe werden von COM synchronisiert und von dem Thread übermittelt, der dem STA zugeordnet ist, in dem das Objekt erstellt wurde. Klassenfactory-Methodenaufrufe werden von dem Thread übermittelt, der dem STA zugeordnet ist, der die Klassenfactory registriert hat. Das Objekt und die Klassenfactory müssen keine Synchronisierung implementieren. Der Implementor muss jedoch den Zugriff auf alle globalen Variablen synchronisieren, die von mehreren STAs verwendet werden. Der Server muss und CoGetInterfaceAndReleaseStream verwendenCoMarshalInterThreadInterfaceInStream, um Schnittstellenzeiger zwischen STAs zu marshallen, möglicherweise von anderen Servern. Der Server kann optional in jeder STA implementieren IMessageFilter , um Aspekte der Anrufübermittlung durch COM zu steuern. Ein degenerierter Fall ist der Singlethreading-Modellserver, der COM-Arbeit in einem STA ausführt.

  2. MTA-Modellserver:

    Der Server arbeitet com in einem oder mehreren Threads, die alle zum MTA gehören. Aufrufe werden nicht von COM synchronisiert. COM erstellt einen Pool von Threads im Serverprozess, und ein Clientaufruf wird von einem dieser Threads übermittelt. Threads müssen keine Nachrichten abrufen und verteilen. Die Objekt- und Klassenfactory muss die Synchronisierung implementieren. Der Server muss keine Schnittstellenzeiger zwischen Threads marshallen.

  3. Mixed Model Server:

    Weitere Informationen finden Sie im Abschnitt "Mixed-Threading-Modell" in diesem Artikel.

Threadingmodelle in Clients

Es gibt drei Kategorien von Clients:

  1. STA-Modellclient:

    Der Client arbeitet com in Threads, die einem oder mehreren STAs zugeordnet sind. Der Client muss und CoGetInterfaceAndReleaseStream verwendenCoMarshalInterThreadInterfaceInStream, um Schnittstellenzeiger zwischen STAs zu marshallen. Ein degenerierter Fall ist der Singlethreading-Modellclient, der COM in einem STA ausführt. Der Clientthread wechselt in eine VOM COM bereitgestellte Nachrichtenschleife, wenn er einen ausgehenden Aufruf vornimmt. Der Client kann verwenden IMessageFilter , um Rückrufe und die Verarbeitung von Fenstermeldungen zu verwalten, während auf ausgehende Aufrufe und andere Parallelitätsprobleme gewartet wird.

  2. MTA-Modellclient:

    Der Client arbeitet com in einem oder mehreren Threads, die alle zum MTA gehören. Der Client muss keine Schnittstellenzeiger zwischen seinen Threads marshallen. Der Client kann nicht verwenden IMessageFilter. Die Threads des Clients werden angehalten, wenn sie einen COM-Aufruf an ein Out-of-Process-Objekt senden, und werden fortgesetzt, wenn der Aufruf zurückgegeben wird. Eingehende Anrufe gehen über COM-erstellte und verwaltete Threads ein.

  3. Gemischter Modellclient:

    Weitere Informationen finden Sie im Abschnitt "Mixed-Threading-Modell" in diesem Artikel.

Threadingmodelle in In-Proc-Servern (DLL-basiert)

Es gibt vier Kategorien von In-Proc-Servern, von denen jede von jedem COM-Client unabhängig vom von diesem Client verwendeten Threadingmodell verwendet werden kann. In-Proc-Server müssen jedoch Marshallingcode für jede benutzerdefinierte (nicht systemdefinierte) Schnittstelle bereitstellen, die sie implementieren, wenn sie die Interoperabilität des Threadingmodells unterstützen sollen. Dies erfordert in der Regel, dass COM die Schnittstelle zwischen Client-Apartments marshallt. Die vier Kategorien sind:

  1. In-proc-Server, der SingleThreading ("Standard" STA) unterstützt– kein ThreadingModel Wert:

    Auf ein von diesem Server bereitgestelltes Objekt wird erwartet, dass auf dasselbe Client-STA zugegriffen wird, mit dem es erstellt wurde. Darüber hinaus erwartet der Server, dass auf alle zugehörigen Einstiegspunkte, zDllGetClassObject. B. und DllCanUnloadNow, und globale Daten über denselben Thread (der dem Standard STA zugeordnet ist) zugegriffen wird. Server, die vor der Einführung von Multithreading in COM vorhanden waren, gehören zu dieser Kategorie. Diese Server sind nicht für den Zugriff durch mehrere Threads ausgelegt, sodass COM alle vom Server im Standard STA des Prozesses bereitgestellten Objekte erstellt und Aufrufe der Objekte von dem Thread übermittelt werden, der dem Standard STA zugeordnet ist. Andere Client-Wohnungen erhalten über Proxys Zugriff auf das Objekt. Aufrufe aus den anderen Wohnungen gehen vom Proxy an den Stub im Standard STA (threadübergreifendes Marshalling) und dann an das -Objekt. Durch dieses Marshalling kann COM Aufrufe des -Objekts synchronisieren, und Aufrufe werden von der STA übermittelt, in der das Objekt erstellt wurde. Das Marshalling zwischen Threads ist relativ zum direkten Aufrufen langsam, daher wird empfohlen, diese Server so umzuschreiben, dass sie mehrere STAs (Kategorie 2) unterstützen.

  2. In-proc-Server, der das Singlethread-Apartmentmodell (mehrere STAs) unterstützt – gekennzeichnet mit ThreadingModel=Apartment:

    Auf ein von diesem Server bereitgestelltes Objekt wird erwartet, dass auf dasselbe Client-STA zugegriffen wird, mit dem es erstellt wurde. Daher ähnelt es einem Objekt, das von einem Singlethread-In-Proc-Server bereitgestellt wird. Von diesem Server bereitgestellte Objekte können jedoch in mehreren STAs des Prozesses erstellt werden, sodass der Server seine Einstiegspunkte wie DllGetClassObject und DllCanUnloadNowund globale Daten für die Verwendung mit Mehrerenthreads entwerfen muss. Wenn beispielsweise zwei STAs eines Prozesses gleichzeitig zwei Instanzen des in-proc-Objekts erstellen, DllGetClassObject können beide STAs gleichzeitig aufrufen. Ebenso muss geschrieben werden, damit der Server vor dem Entladen geschützt wird, DllCanUnloadNow während code noch auf dem Server ausgeführt wird.

    Wenn der Server nur eine instance der Klassenfactory bereitstellt, um alle Objekte zu erstellen, muss die Implementierung der Klassenfactory auch für die Verwendung mit MehrerenThreads konzipiert werden, da mehrere Client-STAs darauf zugreifen. Wenn der Server bei jedem DllGetClassObject Aufruf eine neue instance der Klassenfactory erstellt, muss die Klassenfactory nicht threadsicher sein. Der Implementor muss jedoch den Zugriff auf alle globalen Variablen synchronisieren.

    COM-Objekte, die von der Klassenfactory erstellt werden, müssen nicht threadsicher sein. Der Zugriff auf globale Variablen muss jedoch vom Implementor synchronisiert werden. Nach der Erstellung durch einen Thread wird immer über diesen Thread auf das Objekt zugegriffen, und alle Aufrufe des Objekts werden von COM synchronisiert. Client-Apartments, die sich von dem STA unterscheiden, in dem das Objekt erstellt wurde, müssen über Proxys auf das Objekt zugreifen. Diese Proxys werden erstellt, wenn der Client die Schnittstelle zwischen seinen Wohnungen marshallt.

    Jeder Client, der ein STA-Objekt über seine Klassenfactory erstellt, erhält einen direkten Zeiger auf das Objekt. Dies unterscheidet sich von Einzelthread-In-Proc-Objekten, bei denen nur die Standard STA des Clients einen direkten Zeiger auf das Objekt erhält und alle anderen STAs, die das Objekt erstellen, über einen Proxy Zugriff auf das Objekt erhalten. Da das Marshalling zwischen Threads relativ zum direkten Aufrufen langsam ist, kann die Geschwindigkeit verbessert werden, indem ein Singlethread-In-Proc-Server so geändert wird, dass er mehrere STAs unterstützt.

  3. In-proc-Server, der nur MTA unterstützt – gekennzeichnet mit ThreadingModel=Free:

    Ein von diesem Server bereitgestelltes Objekt ist nur für den MTA sicher. Es implementiert eine eigene Synchronisierung und wird von mehreren Clientthreads gleichzeitig aufgerufen. Dieser Server weist möglicherweise ein Verhalten auf, das mit dem STA-Modell nicht kompatibel ist. (Beispielsweise durch die Verwendung der Windows-Nachrichtenwarteschlange auf eine Weise, die die Nachrichtenpumpe eines STA unterbricht.) Durch das Markieren des Threadingmodells des Objekts als "Free" gibt der Implementor des Objekts Außerdem Folgendes an: Dieses Objekt kann von jedem Clientthread aufgerufen werden, aber dieses Objekt kann auch Schnittstellenzeiger direkt (ohne Marshalling) an alle Threads übergeben, die es erstellt hat, und diese Threads können Aufrufe über diese Zeiger ausführen. Wenn der Client also einen Schnittstellenzeiger an ein vom Client implementiertes Objekt (z. B. eine Senke) an dieses Objekt übergibt, kann er den Rückruf über diesen Schnittstellenzeiger von jedem thread aus aufrufen, den er erstellt hat. Wenn der Client ein STA ist, tritt ein direkter Aufruf von einem Thread, der sich von dem Thread unterscheidet, der das Senkenobjekt erstellt hat, einen Fehler auf (wie in 2 oben gezeigt). Daher stellt COM immer sicher, dass Clients in Threads, die einem STA zugeordnet sind, nur über einen Proxy Zugriff auf diese Art von In-Proc-Objekt erhalten. Außerdem sollten diese Objekte nicht mit dem Freethread-Marshaller aggregiert werden, da sie auf diese Weise direkt in STA-Threads ausgeführt werden können.

  4. In-proc-Server, der Apartmentmodell und Freethreading unterstützt – gekennzeichnet mit ThreadingModel=Both:

    Ein von diesem Server bereitgestelltes Objekt implementiert eine eigene Synchronisierung und wird von mehreren Clientappartements gleichzeitig aufgerufen. Darüber hinaus wird dieses Objekt direkt erstellt und verwendet, nicht über einen Proxy, in STAs oder dem MTA eines Clientprozesses. Da dieses Objekt direkt in STAs verwendet wird, muss der Server Schnittstellen von Objekten, möglicherweise von anderen Servern, zwischen Threads marshallen, damit der Zugriff auf jedes Objekt auf threadinggerechte Weise gewährleistet ist. Durch Das Markieren des Threadingmodells des Objekts als "Both" gibt der Implementor des Objekts Folgendes an: Dieses Objekt kann von jedem Clientthread aufgerufen werden, aber alle Rückrufe von diesem Objekt an den Client werden nur für das Apartment ausgeführt, in dem das Objekt den Schnittstellenzeiger auf das Rückrufobjekt empfangen hat. COM ermöglicht es, ein solches Objekt direkt in einem STA sowie in einem MTA des Clientprozesses zu erstellen.

    Da jedes Apartment, das ein solches Objekt erstellt, immer einen direkten Zeiger anstelle eines Proxyzeigers erhält, ThreadingModel "Both" bieten Objekte Leistungsverbesserungen gegenüber ThreadingModel "Free" Objekten, wenn sie in ein STA geladen werden.

    Da ein ThreadingModel "Both" Objekt auch für den MTA-Zugriff konzipiert ist (es ist intern threadsicher), kann es die Leistung beschleunigen, indem es mit dem von CoCreateFreeThreadedMarshalerbereitgestellten Marshaller aggregiert. Dieses vom System bereitgestellte Objekt wird in allen aufrufenden Objekten aggregiert, und benutzerdefinierte Marshalls leiten Zeiger auf das Objekt in alle Apartments im Prozess. Clients in einem beliebigen Apartment, egal ob STA oder MTA, können dann direkt und nicht über einen Proxy auf das Objekt zugreifen. Beispielsweise erstellt ein STA-Modellclient das in proc-Objekt in STA1 und marshallt das Objekt in STA2. Wenn das Objekt nicht mit dem Freethread-Marshaller aggregiert wird, erhält STA2 über einen Proxy Zugriff auf das Objekt. Wenn dies der Fall ist, stellt der Freethread-Marshaller STA2 einen direkten Zeiger auf das Objekt bereit.

    Hinweis

    Beim Aggregieren mit dem Freithreadmarshaller ist Vorsicht zu beachten. Angenommen, ein Objekt, das als ThreadingModel "Both" markiert ist (und auch mit dem Freethread-Marshaller aggregiert), verfügt über einen Datenmember, der einen Schnittstellenzeiger auf ein anderes Objekt mit ThreadingModel dem Wert "Apartment" darstellt. Gehen Sie dann davon aus, dass ein STA das erste Objekt erstellt, und während der Erstellung erstellt das erste Objekt das zweite Objekt. Gemäß den oben beschriebenen Regeln enthält das erste Objekt nun einen direkten Zeiger auf das zweite Objekt. Angenommen, die STA marshallt den Schnittstellenzeiger auf das erste Objekt in ein anderes Apartment. Da das erste Objekt mit dem Freithread-Marshaller aggregiert wird, wird ein direkter Zeiger auf das erste Objekt auf das zweite Apartment gegeben. Wenn das zweite Apartment dann über diesen Zeiger aufruft und dieser Aufruf bewirkt, dass das erste Objekt über den Schnittstellenzeiger auf das zweite Objekt aufruft, ist ein Fehler aufgetreten, da das zweite Objekt nicht direkt aus dem zweiten Apartment aufgerufen werden soll. Wenn das erste Objekt anstelle eines direkten Zeigers einen Zeiger auf einen Proxy auf das zweite Objekt enthält, führt dies zu einem anderen Fehler. Systemproxys sind auch COM-Objekte, die nur einem Apartment zugeordnet sind. Sie behalten ihre Wohnung im Auge, um bestimmte Zirkularitäten zu vermeiden. Ein Objekt, das auf einem Proxy aufruft, der einem anderen Apartment als dem Thread zugeordnet ist, in dem das Objekt ausgeführt wird, erhält also die RPC_E_WRONG_THREAD Rückgabe vom Proxy, und der Aufruf schlägt fehl.

Threadingmodellinteroperabilität zwischen Clients und In-Process-Objekten

Alle Kombinationen der Threading-Modellinteroperabilität sind zwischen Clients und In-Process-Objekten zulässig.

COM ermöglicht es allen STA-Modellclients, mit Einzelthreading-In-Proc-Objekten zu arbeiten, indem das Objekt im Standard STA des Clients erstellt und darauf zugegriffen wird und es an die Client-STA gemarshallt wird, die aufgerufen hatCoCreateInstance[Ex].

Wenn ein MTA in einem Client einen stac-Modell-In-Proc-Server erstellt, startet COM eine "Host"-STA im Client. Dieser Host-STA erstellt das -Objekt, und der Schnittstellenzeiger wird zurück an den MTA gemarshallt. Wenn ein STA einen MTA-In-Proc-Server erstellt, startet COM ebenfalls einen Host-MTA, in dem das Objekt erstellt und zurück in die STA gemarshallt wird. Die Interoperabilität zwischen dem Singlethreading-Modell und dem MTA-Modell wird auf ähnliche Weise behandelt, da das Singlethreading-Modell nur ein degenerierter Fall des STA-Modells ist.

Marshallingcode muss für jede benutzerdefinierte Schnittstelle bereitgestellt werden, die ein proc-Server implementiert, wenn er die Interoperabilität unterstützen möchte, die COM zum Marshallen der Schnittstelle zwischen Clientapartments erfordert. Weitere Informationen finden Sie weiter unten im Abschnitt REFERENCES.

Beziehung zwischen zurückgegebenem Threadingmodell und Class Factory-Objekt

Eine genaue Definition von In-Proc-Servern, die in Wohnungen "geladen" werden, wird in den beiden folgenden Schritten erläutert:

  1. Wenn die DLL, die die in-proc-Serverklasse enthält, nicht zuvor vom Betriebssystemladeprogramm geladen (dem Prozessadressraum zugeordnet) wurde, wird dieser Vorgang ausgeführt, und COM ruft die Adresse der von der DLLGetClassObject DLL exportierten Funktion ab. Wenn die DLL zuvor von einem Thread geladen wurde, der einem Apartment zugeordnet ist, wird diese Phase übersprungen.

  2. COM verwendet den Thread (oder im Fall des MTA einer der Threads), der dem "Loading"-Apartment zugeordnet ist, um die Funktion aufzurufen, die DllGetClassObject von der DLL exportiert wird, die nach der CLSID der erforderlichen Klasse fragt. Das zurückgegebene Factoryobjekt wird dann verwendet, um Instanzen von -Objekten der -Klasse zu erstellen.

    Der zweite Schritt (der Aufruf von DllGetClassObject durch COM) erfolgt jedes Mal, wenn ein Client aufruft CoGetClassObject/CoCreateIntance[Ex], auch aus demselben Apartment. Anders ausgedrückt: Kann von einem Thread, der demselben Apartment zugeordnet ist, DllGetClassObject mehrmals aufgerufen werden. Alles hängt davon ab, wie viele Clients in diesem Apartment versuchen, Zugriff auf ein Klassenfactoryobjekt für diese Klasse zu erhalten.

Autoren von Klassenimplementierungen und letztendlich der Autor des DLL-Pakets einer bestimmten Gruppe von Klassen haben die vollständige Freiheit zu entscheiden, welches Factoryobjekt als Reaktion auf den DllGetClassObject Funktionsaufruf zurückgegeben werden soll. Der Autor des DLL-Pakets hat das ultimative Wort, da der Code "hinter" dem einzelnen DLL-weiten DllGetClassObject() Einstiegspunkt das erste und potenziell letzte Recht hat, zu entscheiden, was zu tun ist. Die drei typischen Möglichkeiten sind:

  1. DllGetClassObject gibt ein eindeutiges Klassenfactoryobjekt pro aufrufenden Thread zurück (dies bedeutet ein Klassenfactoryobjekt pro STA und potenziell mehrere Klassenfactorys innerhalb des MTA).

  2. DllGetClassObject Gibt immer dasselbe Klassenfactoryobjekt zurück, unabhängig von der Identität des aufrufenden Threads oder der Art des Dem aufrufenden Thread zugeordneten Apartments.

  3. DllGetClassObject gibt ein eindeutiges Klassenfactoryobjekt pro aufrufenden Apartment zurück (eines pro Apartment in STA und MTA).

Es gibt weitere Möglichkeiten für die Beziehung zwischen Aufrufen DllGetClassObject von und dem zurückgegebenen Klassenfactoryobjekt (z. B. ein neues Klassenfactoryobjekt pro Aufruf von DllGetClassObject), aber diese scheinen derzeit nicht nützlich zu sein.

Zusammenfassung der Client- und Objektthreadingmodelle für In-Proc Server

In der folgenden Tabelle wird die Interaktion zwischen verschiedenen Threadingmodellen zusammengefasst, wenn ein Clientthread zum ersten Mal eine Klasse aufruft CoGetClassObject , die als In-Proc-Server implementiert ist.

Arten von Clients/Threads:

  • Der Client wird in einem Thread ausgeführt, der dem STA "Standard" (erster aufzurufender CoInitialize Thread oder CoInitializeEx mit COINIT_APARTMENTTHREADED Flag) zugeordnet ist. Rufen Sie diese STA0 auf (auch als Singlethreading-Modell bezeichnet).
  • Client wird in einem Thread ausgeführt, der in jedem anderen STA [ASCII 150] Aufruf dieses STA* zugeordnet ist.
  • -Client wird in einem Thread ausgeführt, der im MTA zugeordnet ist.

Arten von DLL-Servern:

  • Der Server hat keinen ThreadingModel Schlüssel– nennen Sie diesen "None".
  • Der Server ist als "Apartment" gekennzeichnet. Nennen Sie dies "Apt".
  • Der Server ist als "Free" gekennzeichnet.
  • Der Server ist als "Beide" gekennzeichnet.

Beachten Sie beim Lesen der folgenden Tabelle die oben vorgenommene Definition des "Ladens" eines Servers in eine Wohnung.

Client         Server                 Result
STA0           None                   Direct access; server loaded into STA0  
STA*           None                   Proxy access; server loaded into STA0.  
MTA            None                   Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary;  
STA0           Apt                    Direct access; server loaded into STA0  
STA*           Apt                    Direct access; server loaded into STA*  
MTA            Apt                    Proxy access; server loaded into an STA created automatically by COM.
STA0           Free                   Proxy access; server is loaded into MTA MTA created automatically by COM if necessary.
STA*           Free                   Same as STA0->Free
MTA            Free                   Direct access
STA0           Both                   Direct access; server loaded into STA0
STA*           Both                   Direct access; server loaded into STA*
MTA            Both                   Direct access; server loaded into the MTA

References

SDK-Dokumentation zur - und IMessageFilter -CoRegisterMessageFilter()Schnittstelle.