Programmieren mit C++

Allgemeines

COM

Asynchrones COM

Was ist asynchrones COM?

Frage

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.

Lösung

HRESULT Begin_GetDelta ( [in]  UINT uDeltaID);HRESULT Finish_GetDelta( [out] IDelta** ppDelta);

Ruft der Client die Begin_##()-Methode auf, instruiert er damit das Aufrufobjekt, einen Call abzusetzen. Die Begin_##()-Methode kehrt daher sofort wieder zurück, sodass der HRESULT-Wert, den die Methode als Funktionsergebnis liefert, keinerlei Aussagekraft über den Erfolg der Methode besitzt. Begin_##() leitet vielmehr nur den Methodenaufruf ein. Ist die tatsächliche Methode komplett abgearbeitet, wird das Aufrufobjekt über das Ergebnis des Aufrufs sowie der resultierenden Werte der [out]-Parameter sowie des HRESULT-Funktionsergebnisses informiert. Diese Werte kann der Client erhalten, indem er die Methode Finish_##() aufruft.

Ruft der Client die Finish_##()-Methode auf, bevor die darunter liegende Methode ihre Arbeit abgeschlossen hat, blockiert der Client-Thread bis das Ergebnis vorliegt. Aus diesem Grund ist der Client gut beraten, zunächst einige andere Tasks abzuarbeiten, bevor die Methode Finish_##() aufgerufen wird. Damit ist letztlich auch erklärt, warum es sich hier im Wesentlichen nicht um ein echtes asynchrones Interface, sondern lediglich um non-blocking Calls handelt. Eine komplette asynchrone Schnittstelle müsste einen Informationsmechanismus mitbringen, mit dessen Hilfe der Client über die Abarbeitung der Methode informiert wird, sodass dann gezielt die Finish_##()-Methode aufgerufen werden kann.

Allerdings implementiert das Aufrufobjekt ein ISynchronize-Interface, über das der Client im Polling-Verfahren ermitteln kann, ob die Methode abgearbeitet ist. Zusätzlich kann der Client seine eigene Implementation dieses Interface bereitstellen, sodass unter Rückgriff auf die normalen Win32 Synchronisationsobjekte das Aufrufobjekt die Abarbeitung der Methode signalisieren kann, während der Client-Thread darauf mittels normaler Win32 Wait-Methoden wartet. Darüber hinaus implementiert das Aufrufobjekt ein ICancelMethodCalls Interface, das Methoden der gerade ausgeführten Methode abbricht.

Clientseite

Auf der Serverseite sollte die Komponente das ICallFactory-Interface zum Handling der non-blocking Calls implementieren. COM fragt dieses Interface ab um herauszufinden, ob sich die Komponente asynchron aufrufen lässt. Anschließend erzeugt COM über das Interface ein Aufrufobjekt spezifisch zur Komponente, die COM in ein Synchronisationsobjekt aggregiert und zur Verwaltung des Rückrufs zum Client verwendet. Den Code des Aufrufobjekts und so auch die asynchrone Interface-Version muss der Entwickler liefern.

Erfolgt ein Aufruf eines Aufrufobjekt um einen non-blocking Call abzusetzen, wird ein RPC-Thread verwendet um die entsprechende Begin_##()-Methode aufzurufen. Der selbst geschriebene Code muss dies implementieren, indem der Request an einen ebenfalls implementierten Thread-Pool übergeben wird und der Aufruf dann sofort zurückkehrt. Damit ist der RPC-Thread wieder frei für einen weiteren Call.

Für den Entwickler bleibt bei all den Vorteilen das Implementieren eines Thread-Pools. Ferner ist zu beachten, dass die Thread Funktion das von COM erzeugte Synchronisationsobjekt zu verwenden hat um das Beenden anzuzeigen. Dies veranlasst das System Finish_##() aufzurufen und die Rückgabewerte des Aufrufs an den Client zu returnieren.

Serverseitiges Handling

Als Wermutstropfen erweist sich, dass asynchrone Aufrufe nicht COM+ konfigurierte Objekte verwenden können. Allerdings kann eine konfigurierte Komponente eine unkonfigurierte Komponente asynchron aufrufen.

COM+





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 01:08:04 von textarchiv.alojado.de