|
Asynchrones COM, oder um es korrekt zu bezeichnen non-blocking Calls, sind ein neues Feature des Betriebssystems Windows 2000. Es wurde eingeführt, um das Problem der blockierenden Client- und RPC-Threads während aktiver COM Calls zu lösen.
Das Problem ist altbekannt und systemimmanent: COM ist synchron konzipiert. Dies bedeutet, dass immer, wenn ein Client eine Anfrage an den Server richtet, der Client-Thread solange blockiert ist, bis der Server die Anfrage bearbeitet. Handelt es sich beim Client-Thread um einen UI Thread, hat dies eventuell Auswirkungen auf die Aktualisierungshäufigkeit des Fensters sowie eventuelle Eingaben des Anwenders.
Auf der Serverseite ist das Problem zwar nicht ganz so groß aber trotzdem vorhanden. Ein Server kann ein Multithread Apartment Modell verwenden, um RPC den Einsatz eines Pools von Threads mit normaler Priorität für das Entgegennehmen von Anfragen zu erlauben. Allerdings werden diese Threads verwendet, um die Objektmethode auszuführen, sodass in der Zeit, in der ein RPC-Thread eine COM-Anfrage ausführt, der Thread nicht für andere Aufrufe verfügbar ist.
Des Weiteren kontrolliert RPC die Serverbelastung. Stellt RPC fest, dass der Server sehr stark ausgelastet ist, erzeugt RPC zusätzliche Threads zum Behandeln der Anfragen. Dies erhöht einerseits die Verfügbarkeit, erzeugt andererseits jedoch auch eine starke Systembelastung, wenn mehrere Threads gleichzeitig ausgeführt werden. Deshalb stellt RPC sicher, dass nach einer Reduzierung der Serverauslastung auch die zusätzlichen Threads wieder zerstört werden, um die Anzahl der Threads im Pool in einem handelbaren Rahmen zu halten. Allerdings erlaubt RPC weder das Festlegen der minimalen und maximalen Anzahl der Threads im Pool noch das Spezifizieren der Threadpriorität.
Non-blocking Calls auf der Serverseite erlauben dem Entwickler das Implementieren eines Threadpools speziell für das Ausführen asynchroner Methoden, mit denen die RPC Threads dahingehend entlastet werden, dass sie nur noch die Methodenaufrufe annehmen und diese dann in einer Warteschlange platzieren. Da jetzt die komplette Verantwortung für die Implementation des Threadpools beim Entwickler liegt, kann man selbst entscheiden, wie viele Threads im Pool verwaltet werden und welche Priorität diese besitzen sollen. Scheut man diese Zusatzarbeit, kann man auch über die Methode QueueUserWorkItem() einen vom System bereitgestellten Pool verwenden, der dann allerdings keinerlei Kontrolle über die Threads oder deren Erzeugung erlaubt.
Der große Vorteil von Server- und/oder Client-seitigen non-blocking Calls besteht darin, dass die gleichen Aufrufe erfolgen, egal, ob blocking oder non-blocking Aufrufe erfolgen. Ferner brauchen sich auch Server und Client keinerlei Gedanken darüber zu machen, welches Schema der jeweils andere einsetzt. Der gesamte Mechanismus wird durch die Proxy-Stub-Files realisiert, was allerdings dazu führt, dass für das Interface kein Typenbibliothek-Marshalling möglich ist.
Möchte eine Komponente non-blocking Calls verwenden, muss das Interface in IDL beschrieben und das Attribut async_iid() gesetzt werden. MIDL teilt grundsätzlich mit, dass ein Proxy-Stub-Code für das Interface erzeugt werden muss, wenn es sowohl synchron als auch asynchron aufgerufen wird. Das Proxy-Objekt selbst wird nur für den synchronen Aufruf eingesetzt. Soll das Objekt hingegen asynchron aufgerufen werden, muss der Client beim Proxy ein ICallFactory-Interface anfragen und CreateCall() aufrufen. Dies instruiert den Proxy, ein separates Aufrufobjekt zu erzeugen, das das Proxy non-blocking Interface implementiert.
Das asynchrone Interface besitzt zwei Methoden für jede Methode des synchronen Interfaces: Für jede Methode gibt es eine Begin_##()- und eine Finish_##()-Methode. Die Begin_##()-Methode besitzt als Parameter alle [in]- und [in, out]-Parameter der synchronen Methode.
Die Finish_##()-Methode listet als Parameter alle [in, out]- und [out]-Parameter auf. Der tatsächliche Rückgabewert der Methode entspricht dem Rückgabewert der Finish_##()-Methode. Weist das synchrone Interface z.B. die Methode
HRESULT GetDelta([in] UINT uDeltaID,
[out] IDelta** ppDelta);
auf, so besitzt das asynchrone Interface die beiden nachfolgenden Methoden.
|