Freigeben über


Beschreibungen und Funktionsweisen von OLE-Threadingmodellen

In diesem Artikel werden OLE-Threadingmodelle beschrieben.

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

Übersicht

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 für die Beschreibung der Beziehung zwischen Objekten und Threads, die Parallelitätsbeziehungen zwischen Objekten, die Mittel, mit denen Methodenaufrufe an ein Objekt übermittelt werden, und die Regeln zum Übergeben von Schnittstellenzeigern zwischen Threads. Komponenten und ihre Kunden wählen zwischen den folgenden beiden Apartmentmodellen, die derzeit von COM unterstützt werden:

  1. Einzelthread-Apartmentmodell (STA): Mindestens ein Threads in einem Prozess verwenden COM und Aufrufe an COM-Objekte werden von COM synchronisiert. Schnittstellen werden zwischen Threads gemarstet. Ein degeneriertes 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 Threads 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 sich systemcode zwischen Aufrufer und Objekt interpositioniert. Da mehrere gleichzeitige Clients Objekte mehr oder weniger gleichzeitig aufrufen (gleichzeitig auf Multiprozessorsystemen), müssen Objekte ihren internen Zustand selbst synchronisieren. Schnittstellen werden nicht zwischen Threads gemarstet. Früher wurde dieses Modell manchmal als "Freithreadmodell" bezeichnet.

  3. Sowohl das STA-Modell als auch das MTA-Modell können im selben 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 unterschiedliche Threadingarchitekturen verwenden, um zusammenzuarbeiten. Sie stellen auch Synchronisierungsdienste für Komponenten bereit, die sie erfordern. Beispielsweise kann ein bestimmtes Objekt so konzipiert werden, dass es nur von einem einzelnen Thread aufgerufen wird und nicht gleichzeitige Aufrufe von Clients synchronisiert werden. Wenn ein solches Objekt gleichzeitig von mehreren Threads aufgerufen wird, stürzt es ab oder verursacht Fehler. COM stellt die Mechanismen für die Interoperabilität von Threadingarchitekturen bereit.

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

Ein "Apartment" hat mehrere zusammenhängende Aspekte. Zunächst handelt es sich um ein logisches Konstrukt für das Überlegen der Parallelität, z. B. die Beziehung von Threads zu einer Gruppe von COM-Objekten. Zweitens muss ein Satz von Regelprogrammierern gehorchen, um das Parallelitätsverhalten zu erhalten, das sie von der COM-Umgebung erwarten. Schließlich handelt es sich um vom System bereitgestellten Code, mit dem Programmierer threadkoncurrency in Bezug auf COM-Objekte verwalten können.

Der Begriff "Wohnung" stammt aus einer Metapher, in der ein Prozess als diskrete Entität konzipiert wird, z. B. ein "Gebäude", das in eine Reihe verwandter, aber unterschiedlicher "Gebietsschemas" unterteilt ist, die als "Wohnungen" bezeichnet wird. Eine Wohnung ist ein "logischer Container", der eine Zuordnung zwischen Objekten und in einigen Fällen Threads erstellt. Threads sind keine Wohnungen, obwohl ein einzelner Thread logisch mit einer Wohnung im STA-Modell verknüpft sein kann. Objekte sind keine Wohnungen, obwohl jedes Objekt einem und nur einer Wohnung zugeordnet ist. Aber Wohnungen sind mehr als nur ein logisches Konstrukt; ihre Regeln beschreiben das Verhalten des COM-Systems. Wenn die Regeln der Apartmentmodelle nicht befolgt werden, funktionieren COM-Objekte nicht ordnungsgemäß.

Weitere Informationen

Ein Singlethreaded Apartment (STA) ist eine Gruppe von COM-Objekten, die einem bestimmten Thread zugeordnet sind. Diese Objekte werden der Wohnung zugeordnet, indem sie vom Thread erstellt wird oder genauer gesagt zuerst dem COM-System (in der Regel durch Marshalling) auf dem Thread ausgesetzt wird. EIN STA wird als Ort betrachtet, an dem ein Objekt oder ein Proxy "lebt". Wenn das Objekt oder der Proxy von einer anderen Wohnung (in demselben oder einem anderen Prozess) aufgerufen werden muss, muss der Schnittstellenzeiger an diese Wohnung gemarstet werden, in der ein neuer Proxy erstellt wird. Wenn die Regeln des Apartmentmodells befolgt werden, sind keine direkten Aufrufe anderer Threads im selben Prozess für dieses Objekt zulässig; dies würde gegen die Regel verstoßen, dass alle Objekte innerhalb einer bestimmten Wohnung auf 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 auf zusätzlichen Threads ausgeführt wird.

Der thread, der einem STA zugeordnet ist, muss Aufrufen CoInitialize oder CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) Senden von Fenstermeldungen für die zugehörigen Objekte zum Empfangen eingehender Anrufe ausführen. COM verteilt und synchronisiert Aufrufe an Objekte in einem STA mithilfe von Fenstermeldungen, wie weiter unten in diesem Artikel beschrieben.

Das Haupt-STA ist der Thread, der innerhalb eines bestimmten Prozesses aufgerufen CoInitialize oder CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) zuerst aufgerufen wird. Das Haupt-STA eines Prozesses muss lebendig bleiben, bis alle COM-Arbeiten abgeschlossen sind, da einige inproc-Objekte immer im Haupt-STA geladen werden, wie weiter unten in diesem Artikel beschrieben.

Windows NT 4.0 und DCOM95 führen eine neue Art von Wohnung ein, die als Multithread-Apartment (MTA) bezeichnet wird. Ein MTA ist eine Gruppe von COM-Objekten, die einem Satz von Threads im Prozess zugeordnet sind, sodass jeder Thread jede Objektimplementierung direkt ohne die Interposition des Systemcodes aufrufen kann. Schnittstellenzeiger auf jedes Objekt im MTA können zwischen den Threads übergeben werden, die dem MTA zugeordnet sind, ohne gemarstet werden zu müssen. Alle Threads im Prozess, die aufgerufen CoInitializeEx(NULL, COINIT_MULTITHREADED) werden, sind dem MTA zugeordnet. Im Gegensatz zum oben beschriebenen STA müssen die Threads in einem MTA keine Fenstermeldungen für die zugehörigen 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ädigung schützen, und sie können keine Annahmen über den Inhalt von Thread-Local Storage machen, der zwischen verschiedenen Methodenaufrufen konstant bleibt.

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

Ein Prozess wählt eines der Threadingmodelle aus, die von COM bereitgestellt werden. Ein STA-Modellprozess verfügt über einen oder mehrere STAs und verfügt nicht über einen MTA. Ein MTA-Modellprozess verfügt über einen MTA mit einem oder mehreren Threads und verfügt nicht über STAs. Ein gemischter Modellprozess verfügt über einen MTA und eine beliebige Anzahl von STAs.

Singlethread-Apartmentmodell

Der Thread eines STA muss Fensternachrichten aufrufen CoInitialize oder abrufen CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) und verteilen, da COM Fensternachrichten verwendet, um die Übermittlung von Aufrufen an ein Objekt in diesem Modell zu synchronisieren und zu verteilen. Weitere Informationen finden Sie im Abschnitt "VERWEISE" weiter unten.

Server, der STA-Modell unterstützt:

Im STA-Modell werden Aufrufe eines Objekts von COM auf die gleiche Weise synchronisiert wie Fenstermeldungen, die in einem Fenster gepostet wurden. Aufrufe werden mithilfe von Fenstermeldungen an den Thread übermittelt, der das Objekt erstellt hat. Folglich muss der Thread des Objekts Aufrufen Get/PeekMessage und DispatchMessage Empfangen von Aufrufen ausführen. COM erstellt ein ausgeblendetes Fenster, das den einzelnen STA zugeordnet ist. Ein Aufruf eines Objekts von außerhalb des STA wird von der COM-Laufzeit an den Thread des Objekts mithilfe einer in diesem ausgeblendeten Fenster geposteten Fenstermeldung übertragen. Wenn der thread, der dem STA des Objekts zugeordnet ist, die Nachricht abruft und verteilt, empfängt die Fensterprozedur für das ausgeblendete Fenster, auch von COM implementiert, sie. Die Fensterprozedur wird von der COM-Laufzeit verwendet, um den dem STA zugeordneten Thread zu "hooken", da sich die COM-Laufzeit auf beiden Seiten des Aufrufs vom COM-eigenen Thread zum STA-Thread befindet. Die COM-Laufzeit (die nun im STA-Thread ausgeführt wird) ruft "up" über einen COM-bereitgestellten Stub in die entsprechende Schnittstellenmethode des Objekts auf. Der vom Methodenaufruf zurückgegebene Ausführungspfad kehrt den "up"-Aufruf um; der Aufruf kehrt zum Stub und zur COM-Laufzeit zurück, wodurch die Steuerung über eine Fenstermeldung an den COM-Laufzeitthread übergeben wird, der dann über den COM-Kanal an den ursprünglichen Aufrufer zurückgibt.

Wenn mehrere Clients ein STA-Objekt aufrufen, werden die Aufrufe automatisch durch die Übertragung des im STA verwendeten Kontrollmechanismus in die Nachrichtenwarteschlange eingereiht. Das Objekt empfängt bei jedem Abrufen und Verteilen von Nachrichten einen Aufruf. Da die Aufrufe auf diese Weise von COM synchronisiert werden und die Aufrufe immer im einzelnen Thread übermittelt werden, der dem STA des Objekts zugeordnet ist, müssen die Schnittstellenimplementierungen des Objekts keine Synchronisierung bereitstellen.

Notiz

Das Objekt kann erneut eingegeben werden, wenn eine Schnittstellenmethodenimplementierung Meldungen abruft und verteilt, während ein Methodenaufruf verarbeitet wird, wodurch ein weiterer Aufruf an das Objekt durch dasselbe STA übermittelt wird. Dies ist häufig der Fall, wenn ein STA-Objekt einen ausgehenden (cross-apartment/cross-process)-Aufruf mithilfe von COM ausführt. Dies ist identisch mit der Art und Weise, in der eine Fensterprozedur erneut eingegeben werden kann, wenn sie Nachrichten abruft und versendet, während eine Nachricht verarbeitet wird. COM verhindert nicht den erneuten Eingang im selben Thread, verhindert aber gleichzeitige Ausführung. Sie bietet auch eine Möglichkeit, mit der COM-bezogene Reentranz verwaltet werden kann. Weitere Informationen finden Sie im Abschnitt "VERWEISE" weiter unten. Das Objekt wird nicht erneut eingegeben, wenn Methodenimplementierungen nicht aus seiner Wohnung herausgerufen werden oder nachrichten anderweitig abrufen und verteilen.

Kundenverantwortung im STA-Modell:

Clientcode, der in einem Prozess und/oder Thread ausgeführt wird, der das STA-Modell verwendet, muss schnittstellen eines Objekts zwischen Wohnungen mithilfe CoMarshalInterThreadInterfaceInStream und CoGetInterfaceAndReleaseStream. Wenn apartment 1 im Client z. B. einen Schnittstellenzeiger hat und Apartment 2 die Verwendung erfordert, muss Apartment 1 die Schnittstelle mit CoMarshalInterThreadInterfaceInStreammarshallen. Das von dieser Funktion zurückgegebene Streamobjekt ist threadsicher, und der Schnittstellenzeiger sollte in einer direkten Speichervariable gespeichert werden, auf die von Apartment 2 zugegriffen werden kann. Apartment 2 muss diese Datenstromschnittstelle übergeben, um CoGetInterfaceAndReleaseStream die Schnittstelle des zugrunde liegenden Objekts zu entmarsen und einen Zeiger auf einen Proxy zurückzugeben, über den es auf das Objekt zugreifen kann.

Die Hauptwohnung eines bestimmten Prozesses sollte lebendig bleiben, bis der Client alle COM-Arbeiten abgeschlossen hat, da einige inproc-Objekte in die Hauptwohnung geladen werden. (Weitere Informationen finden Sie weiter unten).

Mehrthread-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)wurden.

Notiz

Aktuelle Implementierungen von COM ermöglichen einen Thread, der COM nicht explizit initialisiert, um Teil des MTA zu sein. Ein Thread, der COM nicht initialisiert, ist nur Teil des MTA, wenn er mit der Verwendung von COM beginnt, nachdem mindestens ein anderer Thread im Prozess zuvor aufgerufen CoInitializeEx(NULL, COINIT_MULTITHREADED)wurde. (Es ist sogar möglich, dass COM selbst den MTA initialisiert hat, wenn kein Clientthread dies explizit ausgeführt hat, z. B. einen Thread, der mit sta-Aufrufen CoGetClassObject/CoCreateInstance[Ex] verknüpft ist. auf einer CLSID, die als "ThreadingModel=Free" gekennzeichnet ist, und COM erstellt implizit einen MTA, in den das Klassenobjekt geladen wird.) Informationen zur Threadingmodellinteroperabilität 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 und dann nach Abschluss der COM-Arbeit initialisieren muss.CoUninitialize Die Kosten für die Initialisierung eines MTA "unnötig" sind minimal.

MTA-Threads müssen keine Nachrichten abrufen und verteilen, da COM keine Fenstermeldungen 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 von COM synchronisiert. Mehrere Clients können gleichzeitig ein Objekt aufrufen, das dieses Modell in verschiedenen Threads unterstützt, und das Objekt muss die Synchronisierung in seinen Schnittstellen-/Methodenimplementierungen mithilfe von Synchronisierungsobjekten wie Ereignissen, Mutexes, 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 In-Process-Clients in mehreren Threads empfangen, die dem MTA zugeordnet sind.

  • Kundenverantwortung 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 verwenden, der von einem anderen MTA-Thread als direkter Speicherzeiger abgerufen wird. Wenn ein Clientthread einen Aufruf an ein Out-of-Process-Objekt vorgibt, wird er angehalten, bis der Aufruf abgeschlossen ist. Aufrufe können an Objekten eingehen, die dem MTA zugeordnet sind, während alle mit der MTA verbundenen Anwendungsthreads bei ausgehenden Aufrufen blockiert werden. In diesem Fall und im Allgemeinen werden eingehende Anrufe in Threads übermittelt, die von der COM-Laufzeit bereitgestellt werden. Nachrichtenfilter (IMessageFilter) sind für die Verwendung im MTA-Modell nicht verfügbar.

Gemischte Threadingmodelle

Ein Prozess, der das Gemischtthreadingmodell unterstützt, verwendet ein MTA und mindestens ein STAs. Schnittstellenzeiger müssen zwischen allen Wohnungen gemarstet werden, können jedoch ohne Marshalling innerhalb der MTA verwendet werden. Aufrufe von Objekten in einem STA werden von COM synchronisiert, damit nur ein Thread ausgeführt wird, während Aufrufe von Objekten im MTA nicht vorhanden sind. Aufrufe von sta an MTA durchlaufen jedoch normalerweise vom System bereitgestellten Code und wechseln vom STA-Thread zu einem MTA-Thread, bevor sie an das Objekt übermittelt werden.

Notiz

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

Auswählen der Threadingmodelle

Eine Komponente kann das STA-Modell, das MTA-Modell oder eine Kombination der beiden mithilfe des Mixed-Threading-Modells unterstützen. Beispielsweise kann ein Objekt, das umfangreiche E/A-Vorgänge ausführt, MTA unterstützen, um eine maximale Antwort auf Clients bereitzustellen, indem schnittstellenbasierte Aufrufe während der E/A-Latenz erfolgen können. Alternativ wählt ein Objekt, das mit dem Benutzer interagiert, fast immer die Unterstützung von STA zum Synchronisieren eingehender COM-Aufrufe mit seinen GUI-Vorgängen. 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 Reaktion auf Clients ist besser, da die Synchronisierung für kleinere Codeabschnitte anstelle des gesamten Schnittstellenaufrufs verwendet wird, wie von COM bereitgestellt.

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

Markieren des Unterstützten Threadingmodells von In-Proc-Servern

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

Die CoInitialize APIs bieten apartment control for client code and for objects that are packaged in. EXEs, da der Startcode der COM-Laufzeit COM in der gewünschten Weise initialisieren kann.

Ein inproc(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 diesem kompatibel ist. Ein benannter Wert des aufgerufenen ThreadingModel CLSID\InprocServer32-Schlüssels der Komponente wird für diesen Zweck wie folgt verwendet:

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

Notiz

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 inproc-Server viele Objekttypen (jeweils mit eigener eindeutiger CLSID) bereitstellt, kann jeder Typ einen anderen ThreadingModel Wert aufweisen. Mit anderen Worten, das Threadingmodell ist pro CLSID, nicht pro Codepaket/DLL. Die API-Einstiegspunkte, die für "bootstrap" erforderlich sind, und alle inproc-Server (DLLGetClassObject(), DLLCanUnloadNow()) müssen jedoch threadsicher für jeden in proc-Server sein, der mehrere Threads unterstützt (d. h. einen ThreadingModel Wert von Apartment, Both oder Free).

Wie bereits erwähnt, kennzeichnen Out-of-Process-Server sich nicht selbst mit dem ThreadingModel-Wert. Stattdessen verwenden CoInitialize sie oder CoInitializeEx. DLL-basierte Server, die erwarten, dass die "Ersatz"-Funktionalität von COM (z. B. das vom System bereitgestellte Surrogate DLLHOST.EXE) nicht verarbeitet werden kann, folgen den 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 gerade dann vorwärts, wenn verschiedene Threadingmodelle verwendet werden, da sich der Client und das Objekt in verschiedenen Prozessen befinden und COM daran beteiligt ist, Aufrufe vom Client an das Objekt zu übergeben. Da COM zwischen dem Client und dem Server interposiert ist, stellt sie den Code für die Interoperabilität der Threadingmodelle bereit. Wenn beispielsweise ein STA-Objekt von mehreren STA- oder MTA-Clients gleichzeitig aufgerufen wird, synchronisiert COM die Aufrufe, indem entsprechende Fenstermeldungen in der Nachrichtenwarteschlange des Servers platziert werden. Das STA des Objekts empfängt bei jedem Abrufen und Verteilen von Nachrichten einen Aufruf. Alle Kombinationen der Threadingmodellinteroperabilität sind zulässig und werden vollständig zwischen Clients und Out-of-Process-Objekten unterstützt.

Die Interaktion zwischen einem Client und einem inproc-Objekt, das verschiedene Threadingmodelle verwendet, ist komplizierter. Obwohl der Server in der Proc-Datei ist, muss COM sich in einigen Fällen zwischen dem Client und dem Objekt interposieren. Ein inproc-Objekt zur Unterstützung des STA-Modells kann z. B. von mehreren Threads eines Clients gleichzeitig aufgerufen werden. COM kann es den Clientthreads nicht ermöglichen, direkt auf die Schnittstelle des Objekts zuzugreifen, da das Objekt nicht für einen solchen gleichzeitigen Zugriff konzipiert ist. Stattdessen muss COM sicherstellen, dass Aufrufe synchronisiert und nur vom Thread ausgeführt werden, der dem STA zugeordnet ist, das das Objekt enthält. Trotz der zusätzlichen Komplexität sind alle Kombinationen der Threadingmodellinteroperabilität zwischen Clients und proc-Objekten zulässig.

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

Es folgen drei Kategorien von Out-of-Process-Servern, von denen jeder COM-Client unabhängig vom von diesem Client verwendeten Threadingmodell verwendet werden kann:

  1. STA-Modellserver:

    Der Server funktioniert in einem oder mehreren STAs. Eingehende Aufrufe werden von COM synchronisiert und vom Thread übermittelt, der dem STA zugeordnet ist, in dem das Objekt erstellt wurde. Klassenfactory-Methodenaufrufe werden vom Thread übermittelt, der dem STA zugeordnet ist, der die Klassenfactory registriert hat. Das Objekt und die Klassenfactory müssen keine Synchronisierung implementieren. Der Implementierer muss jedoch den Zugriff auf alle globalen Variablen synchronisieren, die von mehreren STAs verwendet werden. Der Server muss Schnittstellenzeiger( möglicherweise von anderen Servern) zwischen STAs verwenden CoMarshalInterThreadInterfaceInStream und CoGetInterfaceAndReleaseStream marshallen. Der Server kann optional in jedem STA implementiert werden IMessageFilter , um Aspekte der Anrufzustellung durch COM zu steuern. Ein degenerierender Fall ist der Single-Threading-Modellserver, der COM in einem STA funktioniert.

  2. MTA-Modellserver:

    Der Server funktioniert in einem oder mehreren Threads, die alle zum MTA gehören. Anrufe 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. Das Objekt und die Klassenfactory müssen die Synchronisierung implementieren. Der Server muss keine Schnittstellenzeiger zwischen Threads marshallen.

  3. Gemischter Modellserver:

    Weitere Informationen finden Sie im Abschnitt in diesem Artikel mit dem Titel "Mixed-Threading Model".

Threadingmodelle in Clients

Es gibt drei Kategorien von Clients:

  1. STA-Modellclient:

    Der Client funktioniert in Threads, die einem oder mehreren STAs zugeordnet sind. Der Client muss Schnittstellenzeiger zwischen STAs verwenden CoMarshalInterThreadInterfaceInStream und CoGetInterfaceAndReleaseStream marshallen. Ein degenerierender Fall ist der Singlethreading-Modellclient, der COM in einem STA funktioniert. Der Thread des Clients wechselt in eine VON COM bereitgestellte Nachrichtenschleife, wenn er einen ausgehenden Aufruf vorgibt. Der Client kann zum Verwalten von Rückrufen und zur Verarbeitung von Fensternachrichten beim Warten auf ausgehende Anrufe und andere Parallelitätsprobleme verwendet IMessageFilter werden.

  2. MTA-Modellclient:

    Der Client funktioniert in einem oder mehreren Threads, die alle zum MTA gehören. Der Client muss keine Schnittstellenzeiger zwischen den 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 ausführen und fortgesetzt werden, wenn der Aufruf zurückgegeben wird. Eingehende Anrufe kommen auf COM-erstellten und verwalteten Threads an.

  3. Gemischter Modellclient:

    Weitere Informationen finden Sie im Abschnitt in diesem Artikel mit dem Titel "Mixed-Threading Model".

Threadingmodelle in Proc-Servern (DLL-basiert)

Es gibt vier Kategorien von In-Proc-Servern, die jeweils von jedem COM-Client verwendet werden können, unabhängig vom von diesem Client verwendeten Threadingmodell. Inproc-Server müssen jedoch Marshallingcode für alle benutzerdefinierten (nicht systemdefinierten) Schnittstellen bereitstellen, die implementiert werden, wenn sie threadingmodellinteroperabilität unterstützen sollen, da dies in der Regel erfordert, dass COM die Schnittstelle zwischen Clientwohnungen marshallt. Die vier Kategorien sind:

  1. Proc-Server, der single threading ("main" STA) unterstützt– kein ThreadingModel Wert:

    Ein von diesem Server bereitgestelltes Objekt erwartet, dass auf dasselbe Client-STA zugegriffen wird, mit dem es erstellt wurde. Darüber hinaus erwartet der Server, dass alle Einstiegspunkte, z DllGetClassObject . B. und DllCanUnloadNowglobale Daten, vom gleichen Thread (dem dem Haupt-STA zugeordnet) zugegriffen werden. Server, die vor der Einführung von Multithreading in COM vorhanden waren, befinden sich in dieser Kategorie. Auf diese Server kann nicht über mehrere Threads zugegriffen werden, sodass COM alle Vom Server im Haupt-STA des Prozesses bereitgestellten Objekte erstellt und Aufrufe an die Objekte vom Thread übermittelt werden, der dem Haupt-STA zugeordnet ist. Andere Clientwohnungen erhalten Zugriff auf das Objekt durch Proxys. Anrufe von den anderen Wohnungen gehen vom Proxy zum Stub im Haupt-STA (Interthread marshaling) und dann zum Objekt. Mit dieser Marshalling-Funktion kann COM Aufrufe des Objekts synchronisieren und Aufrufe werden vom STA übermittelt, in dem das Objekt erstellt wurde. Interthread marshaling ist relativ zum direkten Aufruf langsam, sodass empfohlen wird, dass diese Server umgeschrieben werden, um mehrere STAs (Kategorie 2) zu unterstützen.

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

    Ein von diesem Server bereitgestelltes Objekt erwartet, dass auf dasselbe Client-STA zugegriffen wird, mit dem es erstellt wurde. Daher ähnelt es einem Objekt, das von einem einzelthreadigen In-Proc-Server bereitgestellt wird. Objekte, die von diesem Server bereitgestellt werden, können jedoch in mehreren STAs des Prozesses erstellt werden, sodass der Server seine Einstiegspunkte entwerfen muss, z DllGetClassObject . B. und DllCanUnloadNowglobale Daten für die Verwendung mit mehreren Threads. Wenn z. B. zwei STAs eines Prozesses gleichzeitig zwei Instanzen des in-proc-Objekts erstellen, DllGetClassObject kann gleichzeitig von beiden STAs aufgerufen werden. Ebenso muss der Server geschrieben werden, damit der Server vor dem Entladen geschützt ist, DllCanUnloadNow während der Code noch auf dem Server ausgeführt wird.

    Wenn der Server nur eine Instanz der Klassenfactory zum Erstellen aller Objekte bereitstellt, muss die Klassenfactoryimplementierung auch für die Verwendung mit mehreren Client-STAs entwickelt werden, da auf sie zugegriffen wird. Wenn der Server bei jedem Aufruf DllGetClassObject eine neue Instanz der Klassenfactory erstellt, muss die Klassenfactory nicht threadsicher sein. Der Implementierer muss jedoch den Zugriff auf alle globalen Variablen synchronisieren.

    COM-Objekte, die von der Klassenfactory erstellt wurden, müssen nicht threadsicher sein. Der Zugriff auf globale Variablen muss jedoch vom Implementierer synchronisiert werden. Nachdem ein Thread erstellt wurde, wird auf das Objekt immer über diesen Thread 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 Kunde die Schnittstelle zwischen seinen Wohnungen marshallt.

    Jeder Client, der ein STA-Objekt über seine Klassenfactory erstellt, ruft einen direkten Zeiger auf das Objekt ab. Dies unterscheidet sich von singlethreaded in-proc-Objekten, wobei nur das Haupt-STA des Clients einen direkten Zeiger auf das Objekt erhält und alle anderen STAs, die das Objekt erstellen, zugriff auf das Objekt über einen Proxy erhalten. Da die Interthreadmarsing relativ zum direkten Aufruf langsam ist, kann die Geschwindigkeit verbessert werden, indem ein singlethreaded in proc-Server geändert wird, um mehrere STAs zu unterstützen.

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

    Ein von diesem Server bereitgestelltes Objekt ist nur für die MTA sicher. Sie implementiert eine eigene Synchronisierung und wird gleichzeitig von mehreren Clientthreads 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 Meldungspumpe eines STA umbricht.) Außerdem wird durch Markieren des Threadingmodells des Objekts als "Frei" angegeben, dass der Implementor des Objekts Folgendes angibt: 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 clientimplantiertes Objekt (z. B. eine Spüle) an dieses Objekt übergibt, kann er sich entscheiden, diesen Schnittstellenzeiger von jedem von ihr erstellten Thread zurückzurufen. Wenn es sich bei dem Client um ein STA handelt, liegt ein direkter Aufruf von einem Thread, der sich vom Thread unterscheidet, der das Sinkobjekt erstellt hat, im Fehler (wie in 2 oben dargestellt). Daher stellt COM immer sicher, dass Clients in Threads, die einem STA zugeordnet sind, nur über einen Proxy Zugriff auf diese Art von proc-Objekt erhalten. Außerdem sollten diese Objekte nicht mit dem Freethread-Marshaler aggregiert werden, da sie direkt auf STA-Threads ausgeführt werden können.

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

    Ein von diesem Server bereitgestelltes Objekt implementiert eine eigene Synchronisierung und wird gleichzeitig von mehreren Clientwohnungen aufgerufen. Darüber hinaus wird dieses Objekt direkt erstellt und anstelle eines Proxys in STAs oder dem MTA eines Clientprozesses verwendet. Da dieses Objekt direkt in STAs verwendet wird, muss der Server Objekte, möglicherweise von anderen Servern, zwischen Threads marshallen, sodass der Zugriff auf jedes Objekt auf threadgerechte Weise gewährleistet ist. Durch das Markieren des Threadingmodells des Objekts als "Beide" 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 erfolgen nur auf der Wohnung, in der 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 threadsicher intern), kann es die Leistung beschleunigen, indem es mit dem von ihnen bereitgestellten CoCreateFreeThreadedMarshalerMarshaler aggregiert wird. Dieses vom System bereitgestellte Objekt wird in allen aufrufenden Objekten aggregiert und benutzerdefinierte Marshals direct pointers to the object in all apartments in the process. Clients in jeder Wohnung, ob sta oder MTA, können dann direkt statt ü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-Marshaler aggregiert wird, erhält STA2 Zugriff auf das Objekt über einen Proxy. Wenn dies der Fall ist, stellt der Freithread-Marshaler STA2 einen direkten Zeiger auf das Objekt bereit.

    Notiz

    Achten Sie beim Aggregieren mit dem Freithread-Marshaler darauf. Gehen Sie beispielsweise davon aus, dass ein Objekt, das als ThreadingModel "Both" (und auch mit dem Freethreaded Marshaler) gekennzeichnet ist, ein Datenmemm aufweist, bei dem es sich um einen Schnittstellenzeiger auf ein anderes Objekt handelt, dessen ThreadingModel Eigenschaft "Apartment" lautet. Gehen Sie dann davon aus, dass ein STA das erste Objekt erstellt und während der Erstellung das zweite Objekt erstellt. Gemäß den oben beschriebenen Regeln hält das erste Objekt nun einen direkten Zeiger auf das zweite Objekt. Gehen Sie nun davon aus, dass das STA den Schnittstellenzeiger auf das erste Objekt zu einem anderen Apartment marshallt. Da das erste Objekt mit dem freithreadigen Marshaler aggregiert wird, wird ein direkter Zeiger auf das erste Objekt auf die zweite Wohnung übergeben. Wenn die zweite Wohnung dann diesen Zeiger aufruft und dieser Aufruf bewirkt, dass das erste Objekt über den Schnittstellenzeiger auf das zweite Objekt aufgerufen wird, ist ein Fehler aufgetreten, da das zweite Objekt nicht direkt von der zweiten Wohnung aufgerufen werden soll. Wenn das erste Objekt einen Zeiger auf einen Proxy auf das zweite Objekt anstelle eines direkten Zeigers hält, führt dies zu einem anderen Fehler. Systemproxys sind auch COM-Objekte, die einem und nur einem Apartment zugeordnet sind. Sie verfolgen ihre Wohnung, um bestimmte Zirkelitäten zu vermeiden. Ein Objekt, das für einen Proxy aufruft, der einem anderen Apartment zugeordnet ist als der Thread, in dem das Objekt ausgeführt wird, empfängt daher die RPC_E_WRONG_THREAD vom Proxy zurückgegeben, und der Aufruf schlägt fehl.

Threadingmodellinteroperabilität zwischen Clients und In-Process-Objekten

Alle Kombinationen der Threadingmodellinteroperabilitä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 Haupt-STA des Clients erstellt und auf das Objekt zugreift und an den clientspezifischen STA gemarsst wird, das aufgerufen wurde CoCreateInstance[Ex].

Wenn ein MTA in einem Client ein STA-Modell in proc-Server erstellt, dreht COM ein "Host"-STA im Client. Dieser Host-STA erstellt das Objekt, und der Schnittstellenzeiger wird zurück zum MTA gemarstet. Wenn ein STA einen MTA-In-Proc-Server erstellt, dreht COM ein Host-MTA, in dem das Objekt erstellt und wieder an das STA gemarstet wird. Die Interoperabilität zwischen dem Single-Threading-Modell und dem MTA-Modell wird ähnlich behandelt, da das Singlethreading-Modell nur ein entgenerierter Fall des STA-Modells ist.

Marshaling-Code muss für jede benutzerdefinierte Schnittstelle bereitgestellt werden, die von einem inproc-Server implementiert wird, wenn er die Interoperabilität unterstützen möchte, die COM benötigt, um die Schnittstelle zwischen Clientapartments zu marshallen. Weitere Informationen finden Sie im Abschnitt "VERWEISE" weiter unten.

Beziehung zwischen Threadingmodell und Class Factory-Objekt zurückgegeben

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

  1. Wenn die DLL, die die proc-Serverklasse enthält, noch nicht vom Betriebssystemladeprogramm geladen (im Prozessadressraum zugeordnet) wurde, wird dieser Vorgang ausgeführt, und COM ruft die Adresse der DLLGetClassObject von der 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 Falle des MTA, eines der Threads), die dem "Loading"-Apartment zugeordnet sind, um die DllGetClassObject von der DLL exportierte Funktion aufzurufen, die die CLSID der erforderlichen Klasse anfordert. Das zurückgegebene Factoryobjekt wird dann verwendet, um Instanzen von Objekten der Klasse zu erstellen.

    Der zweite Schritt (der Aufruf von DllGetClassObject COM) erfolgt jedes Mal, wenn ein Client aufruft CoGetClassObject/CoCreateIntance[Ex], auch innerhalb derselben Wohnung. Mit anderen Worten, kann oft von einem Thread aufgerufen werden, der demselben Apartment zugeordnet ist. Alles hängt davon ab, DllGetClassObject wie viele Clients in dieser Wohnung 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 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" des einzelnen DLL-weiten DllGetClassObject() Einstiegspunkts das erste und potenziell endgültige 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 (d. h. ein Klassenfactoryobjekt pro STA und potenziell mehrere Klassenfabriken innerhalb der MTA).

  2. DllGetClassObject gibt immer das gleiche Klassenfactoryobjekt zurück, unabhängig von der Identität des aufrufenden Threads oder der Art von Apartment, die dem aufrufenden Thread zugeordnet ist.

  3. DllGetClassObject gibt ein eindeutiges Klassen-Factoryobjekt pro aufrufender Wohnung zurück (eine pro Wohnung in STA und MTA).

Es gibt weitere Möglichkeiten für die Beziehung zwischen Aufrufen und DllGetClassObject dem zurückgegebenen Klassenfactoryobjekt (z. B. ein neues Klassenfactoryobjekt pro Aufruf DllGetClassObject), aber sie 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 zuerst eine Klasse aufruft CoGetClassObject , die als In-Proc-Server implementiert wird.

Arten von Clients/Threads:

  • client is running in a thread associated with the "main" STA (first thread to call CoInitialize or CoInitializeEx with COINIT_APARTMENTTHREADED flag)-call this STA0 (also called single-threading model).
  • Der Client wird in einem Thread ausgeführt, der in einem anderen STA [ASCII 150] zugeordnet ist, dieses STA*aufrufen.
  • Der Client wird in einem Thread ausgeführt, der dem MTA zugeordnet ist.

Arten von DLL-Servern:

  • Der Server hat keinen ThreadingModel Schlüssel, der diese "Keine" aufruft.
  • Der Server ist als "Apartment" gekennzeichnet– nennen Sie diesen "Apt".
  • Der Server ist als "Kostenlos" gekennzeichnet.
  • Der Server ist als "Beides" gekennzeichnet.

Beachten Sie beim Lesen der nachstehenden Tabelle die Oben beschriebene 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 auf der CoRegisterMessageFilter() Und IMessageFilter Schnittstelle.