Multithread-Apartment
In einem Multithread-Apartmentmodell befinden sich alle Threads im Prozess, die als Freethreading initialisiert wurden, in einem einzelnen Apartment. Daher ist es nicht erforderlich, zwischen Threads zu marshallen. Die Threads müssen keine Nachrichten abrufen und senden, da COM keine Fenstermeldungen in diesem Modell verwendet.
Aufrufe von Methoden von Objekten im Multithread-Apartment können in jedem Thread im Apartment ausgeführt werden. Es erfolgt keine Serialisierung von Aufrufen. viele Aufrufe können gleichzeitig an dieselbe Methode oder an dasselbe Objekt erfolgen. Objekte, die im Multithread-Apartment erstellt werden, müssen jederzeit Aufrufe ihrer Methoden aus anderen Threads verarbeiten können.
Da Aufrufe von -Objekten in keiner Weise serialisiert werden, bietet die Multithread-Objektparallelität die höchste Leistung und nutzt den besten Vorteil der Multiprozessorhardware für thread-, prozessübergreifende und computerübergreifende Aufrufe. Dies bedeutet jedoch, dass der Code für Objekte die Synchronisierung in ihren Schnittstellenimplementierungen bereitstellen muss, in der Regel durch die Verwendung von Synchronisierungsprimitiven wie Ereignisobjekten, kritischen Abschnitten, Mutexen oder Semaphoren, die weiter unten in diesem Abschnitt beschrieben werden. Außerdem kann kein threadspezifischer Zustand im -Objekt (im lokalen Threadspeicher) gespeichert werden, da das -Objekt die Lebensdauer der Threads, die darauf zugreifen, nicht steuert.
Im Folgenden sind einige wichtige Überlegungen zur Synchronisierung für Multithread-Apartments zu beachten:
- COM ermöglicht die Aufrufsynchronisierung nur für Singlethread-Apartments.
- Multithread-Apartments empfangen keine Aufrufe bei Aufrufen (im gleichen Thread).
- Multithread-Apartments können keine eingabesynchronisierten Aufrufe durchführen.
- Asynchrone Aufrufe werden in synchrone Aufrufe in Multithread-Apartments konvertiert.
- Der Nachrichtenfilter wird für keinen Thread in einem Multithread-Apartment aufgerufen.
Um einen Thread als Freethreading zu initialisieren, rufen Sie CoInitializeExauf, und geben Sie COINIT _ MULTITHREADED an. Informationen zum In-Process-Serverthreading finden Sie unter In-Process Server Threading Issues.
Mehrere Clients können gleichzeitig aus verschiedenen Threads ein Objekt aufrufen, das Freethreading unterstützt. In Out-of-Process-Servern mit Freethreading erstellt COM über das RPC-Subsystem einen Threadpool im Serverprozess, und ein Clientaufruf (oder mehrere Clientaufrufe) kann von jedem dieser Threads jederzeit übermittelt werden. Ein Out-of-Process-Server muss auch die Synchronisierung in seiner Klassenfactory implementieren. In-Process-Objekte mit Freethreading können direkte Aufrufe von mehreren Threads des Clients empfangen.
Der Client kann COM-Arbeiten in mehreren Threads durchführen. Alle Threads gehören zum gleichen Multithread-Apartment. Schnittstellenzeiger werden in einem Multithread-Apartment direkt von Thread zu Thread übergeben, sodass Schnittstellenzeiger nicht zwischen ihren Threads gemarshallt werden. Nachrichtenfilter (Implementierungen von IMessageFilter)werden in Multithread-Apartments nicht verwendet. Der Clientthread hält an, wenn er einen COM-Aufruf von Out-of-Apartment-Objekten durchführt, und wird fortgesetzt, wenn der Aufruf zurückgegeben wird. Aufrufe zwischen Prozessen werden weiterhin von RPC verarbeitet.
Threads, die mit dem Freethreadmodell initialisiert wurden, müssen ihre eigene Synchronisierung implementieren. Wie bereits in diesem Abschnitt erwähnt, aktiviert Windows diese Implementierung über die folgenden Synchronisierungsprimitiven:
- Ereignisobjekte bieten eine Möglichkeit, einen oder mehrere Threads zu signalisieren, dass ein Ereignis aufgetreten ist. Jeder Thread innerhalb eines Prozesses kann ein Ereignisobjekt erstellen. Ein Handle für das Ereignis wird von der Ereigniserstellungsfunktion CreateEventzurückgegeben. Nachdem ein Ereignisobjekt erstellt wurde, können Threads mit einem Handle für das Objekt darauf warten, bevor die Ausführung fortgesetzt wird.
- Kritische Abschnitte werden für einen Codeabschnitt verwendet, der exklusiven Zugriff auf einige freigegebene Daten erfordert, bevor sie ausgeführt werden können und die nur von den Threads innerhalb eines einzelnen Prozesses verwendet werden. Ein kritischer Abschnitt ähnelt einem Drehungsstil, durch den jeweils nur ein Thread durchlaufen werden kann und wie folgt funktioniert:
- Um sicherzustellen, dass nicht mehr als ein Thread gleichzeitig auf freigegebene Daten zugreift, ordnet der primäre Thread eines Prozesses eine globale CRITICAL _ SECTION-Datenstruktur zu und initialisiert seine Member. Ein Thread, der einen kritischen Abschnitt eingibt, ruft die EnterCriticalSection-Funktion auf und ändert die Member der Datenstruktur.
- Ein Thread, der versucht, einen kritischen Abschnitt einzugeben, ruft EnterCriticalSection auf, der überprüft, ob die _ CRITICAL SECTION-Datenstruktur geändert wurde. Wenn ja, befindet sich derzeit ein anderer Thread im kritischen Abschnitt, und der nachfolgende Thread wird in den Ruhezustand versetzt. Ein Thread, der einen kritischen Abschnitt verlässt, ruft LeaveCriticalSectionauf, wodurch die Datenstruktur zurückgesetzt wird. Wenn ein Thread einen kritischen Abschnitt verlässt, aktiviert das System einen der im Ruhezustand ausgeführten Threads, der dann in den kritischen Abschnitt eintritt.
- Mutexes führt dieselbe Funktion wie ein kritischer Abschnitt aus, mit der Ausnahme, dass Threads, die in verschiedenen Prozessen ausgeführt werden, auf den Mutex zugreifen können. Das Besitzen eines Mutexobjekts ist so, als hätten Sie den Boden in einer Diskussion. Ein Prozess erstellt ein Mutex-Objekt, indem die CreateMutex-Funktion aufgerufen wird, die ein Handle zurückgibt. Der erste Thread, der ein Mutexobjekt anfordert, erhält den Besitz des Objekts. Wenn der Thread mit dem Mutex fertig ist, wird der Besitz an andere Threads übergeben, die zuerst kommen und zuerst bedient werden.
- Semaphoren werden verwendet, um einen Verweiszähler für einige verfügbare Ressourcen beizubehalten. Ein Thread erstellt ein Semaphor für eine Ressource, indem er die CreateSemaphore-Funktion aufruft und einen Zeiger auf die Ressource, eine anfängliche Ressourcenanzahl und die maximale Ressourcenanzahl übergibt. Diese Funktion gibt ein Handle zurück. Ein Thread, der eine Ressource anfordert, übergibt sein Semaphorhandle in einem Aufruf der WaitForSingleObject-Funktion. Das Semaphorobjekt fragt die Ressource ab, um zu bestimmen, ob sie verfügbar ist. Wenn ja, dekrementiert das Semaphor die Ressourcenanzahl und aktiviert den wartenden Thread. Wenn die Anzahl 0 (null) ist, bleibt der Thread im Stillstand, bis ein anderer Thread eine Ressource freigibt, wodurch das Semaphor die Anzahl auf eins erhöht.