Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Warnung
Deadlock-Risiko bei async/await und blockierenden Aufrufen auf STA-Threads: In verwaltetem Code (.NET) ist die STA-Nachrichtenschleife für die Verarbeitung von COM-Aufrufen von entscheidender Bedeutung. Das Blockieren eines STA-Threads mit Task.Wait(), Task.Result, Thread.Sleep() oder ManualResetEvent.WaitOne() verhindert, dass COM-Rückrufe und apartmentübergreifende Aufrufe zum Abschluss kommen — was einen Deadlock verursacht.
// ❌ DEADLOCK — blocks the STA message loop
[STAThread]
static void Main()
{
var result = GetDataAsync().Result; // Deadlock: awaited continuation
// can't post back to this STA thread
}
// ✅ Correct — use async entry point or pump messages
[STAThread]
static async Task Main()
{
var result = await GetDataAsync(); // continuation resumes on STA via SynchronizationContext
}
Wenn Sie await nicht verwenden können, verwenden Sie stattdessen CoWaitForMultipleHandles (nativ) oder einen Dispatcher-Frame/eine Message Pump (verwaltet) anstelle von rohen Wait-Primitiven bei STA-Threads.
Die Verwendung von Single-Threaded-Apartments (dem Apartmentmodellprozess) stellt ein nachrichtenbasiertes Paradigma für die Handhabung mehrerer parallel ausgeführter Objekte bereit. Es ermöglicht Ihnen, effizienteren Code zu schreiben, indem Sie einen Thread zulassen, während es auf einen zeitaufwendigen Vorgang wartet, um eine weitere Threadausführung zu ermöglichen.
Jeder Thread eines Prozesses, der als Prozess im Apartmentmodell initialisiert wird und Fensternachrichten abruft und verarbeitet, ist ein Single-Thread-Apartment-Thread. Jeder Thread lebt in einer eigenen Wohnung. Innerhalb einer Wohnung können Schnittstellenzeiger ohne Marshalling übergeben werden, und daher kommunizieren alle Objekte in einem Singlethread-Apartmentthread direkt.
Eine logische Gruppierung verwandter Objekte, die alle im selben Thread ausgeführt werden und daher über eine synchrone Ausführung verfügen müssen, könnte sich im selben Singlethread-Apartmentthread befinden. Ein Apartmentmodellobjekt kann sich jedoch nicht in mehr als einem Thread befinden. Aufrufe an Objekte in anderen Threads müssen im Kontext des besitzenden Threads erfolgen; daher übernimmt Distributed COM den Threadwechsel automatisch, wenn Sie einen Proxy aufrufen.
Die Modelle für Interprozess- und Interthread-Kommunikation sind ähnlich. Wenn ein Schnittstellenzeiger an ein Objekt in einem anderen Apartment (in einem anderen Thread) innerhalb desselben Prozesses übergeben werden muss, verwenden Sie dasselbe Marshallingmodell, das Objekte in verschiedenen Prozessen verwenden, um Zeiger über Prozessgrenzen hinweg zu übergeben. Indem Sie einen Zeiger auf das standardmäßige Marshaling-Objekt abrufen, können Sie Schnittstellenzeiger über Threadgrenzen hinweg (zwischen Wohnungen) auf die gleiche Weise wie zwischen Prozessen marshallen. (Schnittstellenzeiger müssen gemarshallt werden, wenn sie zwischen Apartments übergeben werden.)
Die Regeln für Single-Thread-Apartments sind einfach, aber es ist wichtig, sie genau zu befolgen:
- Jedes Objekt sollte nur in einem Thread vorhanden sein (innerhalb eines Single-Thread-Apartments).
- Initialisieren Sie die COM-Bibliothek für jeden Thread.
- Marshallen Sie sämtliche Zeiger auf Objekte, wenn Sie sie zwischen Apartments übergeben.
- Jedes Single-Threaded-Apartment muss über eine Nachrichtenschleife verfügen, um Aufrufe von anderen Prozessen und Apartments innerhalb desselben Prozesses zu verarbeiten. Single-Thread-Apartments ohne Objekte (nur Client) benötigen ebenfalls eine Nachrichtenschleife, um die Broadcast-Nachrichten zu verarbeiten, die von einigen Anwendungen verwendet werden.
- DLL-basierte oder prozessinterne Objekte rufen die COM-Initialisierungsfunktionen nicht auf; Stattdessen registrieren sie ihr Threadingmodell mit dem ThreadingModel benannten Wert unter dem InprocServer32 Schlüssel in der Registrierung. Apartment-unterstützende Objekte müssen auch die Einstiegspunkte von DLLs sorgfältig implementieren. Es gibt besondere Aspekte, die für Threading-In-Process-Server gelten. Weitere Informationen finden Sie unter Threadingprobleme bei In-Process-Servern.
Während mehrere Objekte in einem einzelnen Thread leben können, kann kein Apartmentmodellobjekt auf mehr als einem Thread leben.
Jeder Thread eines Clientprozesses oder Out-of-Process-Servers muss CoInitialize-aufrufen oder CoInitializeEx- aufrufen und COINIT_APARTMENTTHREADED für den dwCoInit Parameter angeben. Das Hauptapartment ist der Thread, der zuerst CoInitializeEx aufruft. Informationen zu In-Process-Servern finden Sie unter Probleme bei der Threadverwaltung von In-Process-Servern.
Alle Aufrufe eines Objekts müssen in seinem Thread (innerhalb seines Apartments) erfolgen. Es ist verboten, ein Objekt direkt aus einem anderen Thread aufzurufen; Die Verwendung von Objekten auf diese freithreadierte Weise kann zu Problemen für Anwendungen führen. Die Folge dieser Regel ist, dass alle Zeiger auf Objekte gemarshallt werden müssen, wenn sie zwischen Apartments übergeben werden. COM stellt die folgenden beiden Funktionen für diesen Zweck bereit:
- CoMarshalInterThreadInterfaceInStream eine Schnittstelle in ein Streamobjekt marshallt, das an den Aufrufer zurückgegeben wird.
- CoGetInterfaceAndReleaseStream demarshallt einen Schnittstellenzeiger aus einem Streamobjekt und gibt ihn frei.
Diese Funktionen umschließen Aufrufe an CoMarshalInterface und CoUnmarshalInterface Funktionen, die die Verwendung der MSHCTX_INPROC-Kennzeichnung erfordern.
Im Allgemeinen wird das Marshalling automatisch von COM durchgeführt. Wenn Sie z. B. einen Schnittstellenzeiger als Parameter bei einem Methodenaufruf über einen Proxy für ein Objekt in einem anderen Apartment übergeben oder CoCreateInstance aufrufen, führt COM das Marshalling automatisch durch. In bestimmten Sonderfällen, in denen der Anwendungsentwickler Schnittstellenzeiger zwischen Apartments übergibt, ohne die normalen COM-Mechanismen zu verwenden, muss der Entwickler das Marshalling manuell durchführen.
Wenn eine Wohnung (Apartment 1) in einem Prozess einen Schnittstellenzeiger hat und eine andere Wohnung (Apartment 2) seine Verwendung erfordert, muss Apartment 1 CoMarshalInterThreadInterfaceInStream aufrufen, um die Schnittstelle zu marshallen. Der von dieser Funktion erstellte Datenstrom ist threadsicher und muss in einer Variablen gespeichert werden, die von Apartment 2 zugänglich ist. Apartment 2 muss diesen Stream an CoGetInterfaceAndReleaseStream übergeben, um das Marshalling der Schnittstelle aufzuheben, und erhält einen Zeiger auf einen Proxy zurück, durch den es auf die Schnittstelle zugreifen kann. Das Hauptapartment muss aktiv bleiben, bis der Client alle COM-Vorgänge abgeschlossen hat (da einige In-Process-Objekte im Hauptapartment geladen werden, wie unter Threadingprobleme bei In-Process-Servern beschrieben). Nachdem ein Objekt auf diese Weise zwischen Threads übergeben wurde, ist es sehr einfach, Schnittstellenzeiger als Parameter zu übergeben. Auf diese Weise übernimmt Distributed COM das Marshalling und den Threadwechsel für die Anwendung.
Um Aufrufe von anderen Prozessen und Apartments innerhalb desselben Prozesses zu verarbeiten, muss jedes Single-Threaded-Apartment über eine Nachrichtenschleife verfügen. Dies bedeutet, dass die Arbeitsfunktion des Threads über eine GetMessage/DispatchMessage-Schleife verfügen muss. Wenn andere Synchronisierungsgrundtypen für die Kommunikation zwischen Threads verwendet werden, kann die MsgWaitForMultipleObjects--Funktion verwendet werden, um sowohl auf Nachrichten als auch auf Threadsynchronisierungsereignisse zu warten. Die Dokumentation für diese Funktion weist ein Beispiel für diese Art von Kombinationsschleife auf.
COM erstellt in jedem Single-Thread-Apartment ein verborgenes Fenster mit der Windows-Klasse „OleMainThreadWndClass“. Ein Aufruf eines Objekts wird als Fenstermeldung für dieses ausgeblendete Fenster empfangen. Wenn die Wohnung des Objekts die Nachricht abruft und sendet, empfängt das ausgeblendete Fenster sie. Die Fensterprozedur ruft dann die entsprechende Schnittstellenmethode des Objekts auf.
Wenn mehrere Clients ein Objekt aufrufen, werden die Aufrufe in der Nachrichtenwarteschlange in eine Warteschlange gestellt, und das Objekt erhält jeweils einen Aufruf, wenn sein Apartment Nachrichten abruft und verarbeitet. Da die Aufrufe von COM synchronisiert werden und die Aufrufe immer vom Thread übermittelt werden, der zur Wohnung des Objekts gehört, müssen die Schnittstellenimplementierungen des Objekts keine Synchronisierung bereitstellen. Single-Thread-Apartments können die Schnittstelle IMessageFilter implementieren, damit sie bei Bedarf Aufrufe abbrechen oder Fensternachrichten empfangen können.
Das Objekt kann erneut aufgerufen werden, wenn eine Implementierung einer seiner Schnittstellenmethoden Nachrichten abruft und weiterleitet oder einen ORPC-Aufruf an einen anderen Thread tätigt und dadurch bewirkt, dass ein weiterer Aufruf an das Objekt zugestellt wird (durch dasselbe Apartment). OLE verhindert nicht die Reentranz auf demselben Thread, kann aber dazu beitragen, Threadsicherheit bereitzustellen. Dies ist identisch mit der Art und Weise, wie eine Fensterprozedur erneut ausgeführt werden kann, wenn sie Nachrichten abruft und versendet, während eine Nachricht verarbeitet wird. Das Aufrufen eines Out-of-Process-Single-Threaded-Apartment-Servers, der einen anderen Single-Threaded-Apartment-Server aufruft, kann jedoch dazu führen, dass der erste Server erneut aufgerufen wird.
Verwandte Themen