COM & Threading„The Basics“
Frank LangeMichael Willers
Microsoft GmbH
Warum COM-Threading? Wie funktioniert’s? Wo gibt’s weitere Info’s?
Agenda
ObjectObject(Instanz 1)(Instanz 1)
Thread 2Thread 2
ObjectObject(Instanz 2)(Instanz 2)
Thread 1Thread 1
Zwei Zwei Threads Threads innerhalb innerhalb eines Prozesses eines Prozesses erzeugen erzeugen das gleiche COM-Object.das gleiche COM-Object.
Ist der COM-Server eine Ist der COM-Server eine DLL (in-proc) dann DLL (in-proc) dann greifen greifen diedie Methoden Methoden beidebeider r ObjektObjektinstanzeninstanzen auf die auf die gleichen gleichen globalen Datenglobalen Daten zuzu..
Sofern dSofern derer ZugriffZugriff nicht nicht threadsafe threadsafe istist “krachts”, “krachts”, wenn beide Threads auf wenn beide Threads auf die globalen Daten die globalen Daten zugreifen.zugreifen.
Glo
bale
Dat
enG
loba
le D
aten
Warum COM-Threading?Ein einfaches Beispiel
?? ??????
Warum COM-Threading?Was bedeutet Threading Model?!
Im “Multithread-Betrieb” können in “bestimmten” Situationen keine direkten Methodenaufrufe erfolgen.
In diesen Situationen ist Marshalling notwendig; Aufrufe werden langsamer!
Wann ist das Marshalling notwendig?
Warum COM-Threading?“Brauchen“ Sie performante Objekte?
Standardmäßig “schützt” COM die Objekte vor dem Zugriff konkurrierender Threads (SingleThreading).• Zugriffe werden synchronisiert (Standard!)• Client braucht nicht darauf zu achten, ob das
Objekt threadsafe ist Bei “echtem” Multithreading muß der
Entwickler dafür sorgen, daß die Objekte threadsafe sind !!!
Threading wird mit Apartments realisiert.
Wie funktioniert‘s?Allgemeines, I
Ein Apartment ist eine Gruppe von Objekten innerhalb eines Prozesses.
Apartments werden benutzt, um eine Zuordnung zwischen Threads und Objekten herzustellen.
Ein Prozeß kann ein oder mehrere Apartments beinhalten.
Wie funktioniert‘s?Was bedeutet Apartment?
Apartment CApartment CApartment BApartment BApartment AApartment A
ProProzess zess YYProProzesszess X X
Wie funktioniert‘s?Beispiele für Apartments
Single-threaded apartments (STAs)• pro Apartment nur ein einziger Thread• beliebig viele STAs pro Prozess• Der innerhalb eines Prozesses zuerst
erzeugte STA heißt main STA Multithreaded apartments (MTAs)
• beliebig viele Threads pro MTA• ein Prozess kann nur genau ein MTA
beinhalten
Wie funktioniert‘s?Was bedeutet Apartment?
Jeder Thread, der mit COM-Objekten arbeitet, muß vorher CoInitialize oder CoInitializeEx aufrufen.• Mit diesem Aufruf “betritt” der Thread ein
Apartment• Jedes Objekt befindet sich in einem
Apartment Objekte in unterschiedlichen Apartments
können nicht “direkt” aufgerufen werden.In diesem Fall ist Marshalling notwendig!• gilt auch innerhalb eines Prozesses!
Wie funktioniert‘s?Allgemeines, II
Thread 1Thread 1
Thread 2Thread 2
mmain STAain STA
STASTA
Thread 3Thread 3
MTAMTA
Thread 4Thread 4
ProzeProze ssss
Marshalling
Wie funktioniert‘s?Objekte, Threads und Apartments
Marshalling
“Ziel-Apartment” ist ein STA…• Aufrufe werden an den “one and only”
Thread des STA weitergeleitet und nacheinander abgearbeitet (serially)
• Aufruf wird mit einer Nachricht realisiert, die an ein von COM erzeugtes “hidden window” gesendet wird (message queuing!)
“Ziel-Apartment” ist ein MTA…• Aufrufe geschehen quasi “gleichzeitig”
(concurrency) via (L)RPC
Wie funktioniert‘s?Aufrufe in andere Apartments
Der 2. Parameter von CoInitializeEx (COINIT-Wert) legt fest, in welchem Apartment-Typ der Thread “läuft”. • COINIT_APARTMENTTHREADED
“setzt” den Thread in ein STA• COINIT_MULTITHREADED “setzt” den
Thread in ein MTA CoInitialize = CoInitializeEx (…,
COINIT_APARTMENTTHREADED )
Wie funktioniert‘s?Zuordnung: Threads und Apartments
Out-of-proc (EXE)• Der Server, in dem sich das Objekt befindet,
bestimmt den Apartmenttyp!• Die main-Funktion des Servers ruft
CoInitialize bzw. CoInitializeEx auf.• Reminder: Jeder Thread, der mit COM-Objekten
arbeitet, muß diese Funktion aufrufen
• D.h.: Der Apartmenttyp wird für alle Objekte des Servers festgelegt!• ACHTUNG: Wenn der Apartmenttyp MTA ist,
müssen alle Objekte des Servers threadsafe sein!
Wie funktioniert‘s?Zuordnung: Objekte und Apartments, I
In-proc (DLL)• Das Threading Model des Objekts
bestimmt den Apartmenttyp!• Das Threading Model ist in der Registry
gespeichert.• D.h.: Der Apartmenttyp wird für jedes
Objekt einzeln festgelegt!
Wie funktioniert‘s?Zuordnung: Objekte und Apartments, II
HKEY_CLASSES_ROOTHKEY_CLASSES_ROOT
CLSIDCLSID
{…}{…}
InprocServer32InprocServer32 ““C:\Servers\MyComp.dll”C:\Servers\MyComp.dll”
““My Component”My Component”
““Apartment”Apartment”““ThreadingModel”ThreadingModel”
An dieser Stelle ist das An dieser Stelle ist das Threading Model eingetragen!Threading Model eingetragen!
Wie funktioniert‘s?Threading Model in der Registry
No ThreadingModel value (=Single)• Alle Objekte “laufen” im main STA eines
Prozesses
Wie funktioniert‘s?Welche Threading Model‘s gibt es?, I
ThreadingModel=Apartment• Objekte können in jedem STA eines
Prozesses “laufen” ThreadingModel=Free
• Objekte “laufen” grundsätzlich nur im“one and only” MTA eines Prozesses
ThreadingModel=Both• Objekte können sowohl in einem STA als
auch im MTA eines Prozesses “laufen”
Wie funktioniert‘s?Welche Threading Model‘s gibt es?, II
COM “nimmt” an, daß Objekte nicht threadsafe sind.
COM “beschränkt” alle Objekt-Instanzen auf das main STA.• Aufrufe von ausserhalb werden
synchronisiert und erfolgen indirekt (marshalling)
Da alle Instanzen im gleichen Thread laufen kann immer nur eine einzige Instanz “Code ausführen”.• Voila! Thread safety!
Wie funktioniert‘s?No Threading Model (=Single)
Thread 1Thread 1 Instanz 1Instanz 1 Instanz 2Instanz 2
Thread 2Thread 2
mmain STAain STA
Alle anderen ApartmentsAlle anderen Apartments
ProxyProxy
Methodenaufrufe werden synchronisiert, Methodenaufrufe werden synchronisiert, da da ALLE Objekt-ALLE Objekt-Instanzen im gleichen Instanzen im gleichen ThreadThread „ „laufen”.laufen”. Somit sind gleichzeitige Somit sind gleichzeitige Zugriffe auf gloZugriffe auf globale Datenbale Daten nicht möglich. nicht möglich.
Marshalling
(gilt für Zugriffe „innerhalb“ von Methoden)(gilt für Zugriffe „innerhalb“ von Methoden)
Wie funktioniert‘s?No Threading Model (=Single)
COM “nimmt” an, daß Objekte “halbwegs” threadsafe sind und “setzt” Objekt in ein STA.
COM synchronisiert Methodenaufrufe innerhalb einzelner Objektinstanzen. Somit sind auch Member einer Instanz geschützt.
Methodenaufrufe unterschiedlicher Instanzen können gleichzeitig erfolgen. Wird innerhalb dieser Methoden auf globale Daten zugegriffen, muß dieser Zugriff threadsafe sein.
Jeder Thread, der in einem STA “läuft” kann dort Objekte erstellen. Zugriffe auf diese Objekte können aus MTA’s nur indirekt erfolgen (Marshalling).
Wie funktioniert‘s?Threading Model=Apartment
Thread 1Thread 1 Instanz 1Instanz 1
Instanz 2Instanz 2Thread 2Thread 2
mmain STAain STA
STASTA
Glo
bal D
aten
Glo
bal D
aten
Globale Daten werden Globale Daten werden nichtnicht vor “überlappenden” vor “überlappenden” Lese- und Schreibzugriffen Lese- und Schreibzugriffen verschiedener verschiedener Instanzen Instanzen geschütztgeschützt und müssen s und müssen synchronisiertynchronisiert werden.werden.
Wie funktioniert‘s?Threading Model=Apartment
COM “nimmt” an, das Objekte threadsafe sind. D.h.: “Concurrent calls” sind OK.• COM braucht Methodenaufrufe nicht zu
synchronisieren COM “setzt” Objekte in den MTA und alle
dortigen Threads können direkt, d.h. ohne Marshalling, auf die Objekte zugreifen.
Threads, die in STAs “laufen” können auf die Objekte im MTA nur indirekt zugreifen (Marshalling!).
Wie funktioniert‘s?Threading Model=Free
COM “nimmt” an, das Objekte grundsätzlich threadsafe sind. Ein Objekt kann in einem STA oder dem MTA angelegt werden.
Dadurch ist sichergestellt, daß das Objekt stets im Apartment des Client-Threads angelegt wird.• Es ist egal, ob der Client ein STA-Thread oder
ein MTA-Thread ist• Der Thread, der das Objekt angelegt hat, kann
stets direkt auf das Objekt zugreifen
Wie funktioniert‘s?Threading Model=Both
““Both”Both”
Kein Eintrag (none)Kein Eintrag (none)““Free”Free”
ThreadingModelThreadingModel
““Apartment”Apartment”DireDirekktt
ProxyProxyDireDirekktt
DireDirekktt
ProxyProxyProxyProxy
DireDirekktt
DireDirekkttProxyProxy
ProxyProxy DireDirekktt DireDirekktt
MTA ThreadMTA Thread
Alle Threads, die CoInitAlle Threads, die CoInitializeializeExExCOINIT_COINIT_MULTITHREADED)MULTITHREADED)aufrufenaufrufen
STA ThreadSTA Thread
Alle weiteren Threads, dieAlle weiteren Threads, die CoInitCoInitializeializeExEx((COINIT_COINIT_APAPARTMENTARTMENTTHREADED)THREADED) aufrufenaufrufen
Main STA ThreadMain STA Thread
AllerersterAllererster Thread, der CoInit Thread, der CoInitializeializeExEx(COINIT_(COINIT_APAPARARTTMENTMENTTHREADED)THREADED)aufruftaufruft
Wie funktioniert‘s?Wann erfolgt Marshalling?
Threading ModelThreading Model AktionAktion
None (Single)None (Single) nichtsnichts
ApartmentApartment Synchronisation für Synchronisation für Methodenzugriffe auf Methodenzugriffe auf globale Datenglobale Daten
FreeFree Synchronisation für Synchronisation für Methodenzugriffe auf Methodenzugriffe auf globale Daten und Instanzdaten (Member)globale Daten und Instanzdaten (Member)
BothBoth Synchronisation für Synchronisation für Methodenzugriffe auf Methodenzugriffe auf globale Daten und Instanzdaten (Member)globale Daten und Instanzdaten (Member)
Wie funktioniert‘s?Was muss implementiert werden?
Es gibt Objekte, die selbst Worker-Threads erzeugen: Diese Threads sollen vorhandene Schnittstellenzeiger benutzen.
Wenn man diese Objekte als "Both" deklariert und in einem STA erzeugt, müssen die Worker-Threads in anderen Apartments erzeugt werden.
Schnittstellenzeiger werden also„gemarshalled“. Dies kann bei häufigen Aufrufen erhebliche Performanceverluste verursachen.
Für diese Fälle kann man mit „Free" die Erzeugung im MTA erzwingen. Die Worker-Threads und das Objekt befinden sich dann im selben Apartment.
Wie funktioniert‘s?Warum „Free“ und „Both“
“No ThreadingModel”-Objekte sind einfach zu implementieren, allerdings ist fast immer Marshalling notwendig.
“ThreadingModel=Apartment”-Objekte verbessern die Performance mit relativ wenig Aufwand.
“ThreadingModel=Free/Both”-Objekte haben die beste Performance, sind aber schwierig zu implementieren und zu debuggen.
Wie funktioniert‘s?Zusammenfassung - Theading Model
Wenn das Threading Model auf Apartment “gesetzt” ist, dann muß der Zugriff auf globale Daten innerhalb von Objekt-Methoden grundsätzlich threadsafe erfolgen.• critical sections• Interlocked-functions
Sollen die Objekte im MTA “laufen” müssen alle Operationen innerhalb des Objekts threadsafe sein.
Wie funktioniert‘s?Zusammenfassung - Objekt
Jeder Thread muß vor dem Benutzen von COM-Objekten CoInitialize(Ex) aufrufen.• Thread “betritt” damit ein Apartment• kein Marshalling bei gleichem Apartmenttyp!
Schnittstellenzeiger dürfen niemals direkt an Threads in anderen Apartments übergeben werden.• Marshalling zwischen unterschiedlichen
Apartments!
Wie funktioniert‘s?Zusammenfassung - Client
Schnittstellenzeiger können zwischen Threads können nur dann direkt übergeben werden, wenn die Threads im gleichen Apartment “liegen”.
Liegen die Threads in unterschiedlichen Appartments ist Marshalling erforderlich.• CoMarshalInterThreadInterfaceInStream
marshals an interface in one thread• CoGetInterfaceAndReleaseStream
unmarshals the interface in another
Wie funktioniert‘s?Marshalling von Schnittstellenzeigern, I
// The following global variable will hold the IStream pointer used by// both threads. COM makes sure the IStream pointer can be safely// used in different apartments.IStream* g_pStream;IMath* pMath;
// Marshal the interface pointer in one thread.CoMarshalInterThreadInterfaceInStream (IID_IFace, pMath, g_pStream); . . .// Unmarshal it in another.IMath* pMath;CoGetInterfaceAndReleaseStream (g_pStream, IID_IFace, (void**) &pMath);
Ein Stream nimmt immer nur einen Schnittstellenzeiger auf!
Wie funktioniert‘s?Marshalling von Schnittstellenzeigern, II
Der FTM ist ein COM-Objekt, das ein “custom marshalling” zur Verfügung stellt.
Auf alle Objekte, die den FTM benutzen, kann grundsätzlich direkt zugegriffen werden• “mehr Speed”, aber:• hebelt Synchronisations-Mechanismen von
COM komplett aus!
Wie funktioniert‘s?Free-Threaded Marshaller (FTM)
Objekte müssen threadsafe sein, um den FTM zu benutzen.• Sofern “externe” Threads direkten Objekt-
zugriff haben, kann COM die Zugriffe nicht synchronisieren!
Der FTM sollte nicht von Objekten benutzt werden, die Objekte in anderen Apartments aufrufen.• Callbacks können Deadlocks erzeugen• Aufrufergebnis kann
RPC_E_WRONG_THREAD sein
Wie funktioniert‘s?Free-Threaded Marshaller (FTM)
Allgemeine Hinweise Multi-Threading: RPC-Call, total
blockierend Single-Threading: Messageloop Deshalb: Kein UI mit MTA!!! In-proc-Server (DLLs) werden beim
Beenden des Prozesses „entsorgt“!! Alternative: CoFreeUnusedLibraries oder
CoFreeAllLibraries aufrufen OLE32.DLL: COM-Runtime OLEAUT32.DLL: Automation, Standard-
Marshalling
Wo gibt’s weitere Info’s? msdn online
• http://www.microsoft.com/germany/msdn Microsoft Systems Journal
• diverse Artikel, z.B. Heft 4/1997 (!) Bücher von WROX Press
• http://www.wrox.com Bücher von Microsoft Press
• Inside COM, Dale Rogerson Knowledge-Base-Artikel Q150777!!!