In-Process Problémy s vlákny serveru
Server v procesu nevolá CoInitialize, CoInitializeExnebo OleInitialize pro označení modelu vláken. Pro objekty dll založené na vláknech nebo v procesu je nutné nastavit model vláken v registru. Výchozí model, pokud nezadáte model vláken, je jedno vlákno na proces. Pokud chcete zadat model, přidáte hodnotu ThreadingModel do klíče InprocServer32 v registru.
Knihovny DLL, které podporují vytváření instancí objektu třídy, musí implementovat a exportovat funkce DllGetClassObject a DllCanUnloadNow. Pokud klient chce instanci třídy, kterou knihovna DLL podporuje, volání CoGetClassObject (buď přímo, nebo prostřednictvím volání CoCreateInstance) volá DllGetClassObject, aby získalo ukazatel na její objekt třídy, když je objekt implementován v knihovně DLL. DllGetClassObject by proto mělo být možné předat více třídních objektů nebo jeden objekt bezpečný pro vlákno (v podstatě pouze pomocí InterlockedIncrement/InterlockedDecrement na jejich interní referenční počty).
Jak název napovídá, DllCanUnloadNow se volá k určení, zda je knihovna DLL, která toto implementuje, používána, což umožňuje volajícímu ji bezpečně uvolnit, pokud používána není. Volání CoFreeUnusedLibraries z libovolného vlákna je vždy směrováno přes vlákno hlavní jednotky pro volání DllCanUnloadNow.
Stejně jako ostatní servery můžou být procesové servery jednovláknové, vnitrové nebo bezvláknové. Tyto servery může používat jakýkoli klient OLE bez ohledu na model vláken používaný tímto klientem.
Mezi klienty a objekty v procesu jsou povoleny všechny kombinace interoperability modelu vláken. Interakce mezi klientem a objektem v procesu, který používá různé modely threadingu, je přesně stejná jako interakce mezi klienty a procesovými servery. Pokud se model vláken klienta a serveru běžícího v rámci procesu liší, musí se COM vložit mezi klienta a objekt.
Pokud objekt v procesu, který podporuje model s jedním vláknem, je volán současně více vlákny klienta, com nemůže klientským vláknům povolit přímý přístup k rozhraní objektu "objekt nebyl navržen pro takový přístup. Místo toho musí COM zajistit, že volání jsou synchronizována a prováděna pouze vláknem klienta, který vytvořil objekt. Com proto vytvoří objekt v hlavním bytě klienta a vyžaduje, aby všechny ostatní klientské apartmány přistupovaly k objektu pomocí proxy serverů.
Když klient v rámci modelu vícevláknového bytu vytvoří v procesu server s bytem orientovaným na vlákna, COM spustí v klientovi hostitelské vlákno s jednovláknovým modelem bytu. Toto hostitelské vlákno vytvoří objekt a ukazatel rozhraní bude předán zpět do klientova apartmánu s volným vláknem. Podobně, když jednovláknový apartmán v klientovi modelu apartmánu vytvoří server běžící na vícevláknovém vlákne procesu, COM spustí hostitelské vlákno pro vícevláknová prostředí (vícevláknový apartmán, ve kterém bude vytvořen objekt, a potom bude zařazen zpět do jednovláknového apartmánu klienta).
Poznámka
Obecně platí, že pokud navrhujete vlastní rozhraní pro server běžící v rámci procesu, měli byste mu také poskytnout kód pro marshalling, aby COM mohl zprostředkovat rozhraní mezi klientskými apartmány.
COM pomáhá chránit přístup k objektům poskytovaným jednovláknovou DLL tím, že vyžaduje přístup ze stejného klientského apartmánu, ve kterém byly vytvořeny. Kromě toho by měl všechny vstupní body knihovny DLL (například DllGetClassObject a DllCanUnloadNow) a globální data vždy přistupovat pomocí stejného vlákna. COM vytváří takové objekty v hlavním vláknu klienta a dává tímto hlavnímu vláknu přímý přístup k ukazatelům objektů. Volání z ostatních apartmánů používají pro přenos dat mezi vlákny marshaling, který se přesouvá z proxy ke stubu v hlavním apartmánu a poté k objektu. To umožňuje modelu COM synchronizovat volání objektu. Volání mezi vlákny jsou pomalá, proto doporučujeme přepsat tyto servery, aby podporovaly práci s více apartmány.
Podobně jako jednovláknový in-processový server musí být objekt poskytovaný DLL knihovnou modelu apartmánu přístupný týmž klientským apartmánem, ze kterého byl vytvořen. Objekty poskytované tímto serverem však lze vytvořit v několika prostředích klienta, takže server musí implementovat své vstupní body (například DllGetClassObject a DllCanUnloadNow) pro použití ve vícero vláknech. Pokud se například dva apartmány klienta pokusí vytvořit dvě instance objektu v rámci procesu současně, DllGetClassObject může být vyvolán současně oběma apartmány. DllCanUnloadNow musí být zapsán tak, aby nedošlo k vyložení knihovny DLL, zatímco se v ní stále spouští kód.
Pokud knihovna DLL poskytuje pouze jednu instanci objektu pro vytváření tříd pro vytvoření všech objektů, musí být implementace objektu pro vytváření tříd také navržena pro vícevláknové použití, protože k ní bude přistupovat více klientských apartmánů. Pokud knihovna DLL vytvoří novou instanci objektu pro vytváření tříd při každém volání DllGetClassObject, nemusí být továrna třídy bezpečná pro přístup z více vláken.
Objekty vytvořené továrnou tříd nemusí být zajištěny proti přístupu z více vláken. Po vytvoření vláknem je objekt vždy přístupný prostřednictvím daného vlákna a všechna volání objektu jsou synchronizována objektem COM. Apartmánový model apartmánu klienta, který vytvoří tento objekt, získá přímý ukazatel na objekt. Klientské apartmány, které se liší od bytu, ve kterém byl objekt vytvořen, musí přistupovat k objektu prostřednictvím proxy serverů. Tyto proxy servery se vytvoří, když klient zařadí rozhraní mezi jeho apartmány.
Pokud je v procesu dll ThreadingModel hodnota nastavena na "Both", objekt poskytovaný touto knihovnou DLL lze vytvořit a použít přímo (bez proxy) v jednovláknových nebo vícevláknových klientských apartmánech. Lze však použít přímo v bytě, ve kterém byl vytvořen. Chcete-li objekt dát jinému bytu, musí být objekt zařazován. Objekt DLL musí implementovat vlastní synchronizaci a může být přístupný více klientských apartmánů najednou.
Aby se zrychlil výkon při volném přístupu k objektům DLL v procesu, COM poskytuje funkci CoCreateFreeThreadedMarshaler. Tato funkce vytvoří vícevláknový zařazovací objekt, který lze integrovat s objektem serveru pracujícího v procesu. Když klientský apartmán ve stejném procesu potřebuje přístup k objektu v jiném apartmánu, bezvláknový marshal poskytuje klientovi přímý ukazatel na objekt serveru, nikoli na proxy server, když klient marshaluje rozhraní objektu do jiného apartmánu. Klient nemusí provádět žádnou synchronizaci. To funguje pouze v rámci stejného procesu; Standardní zařazování se používá pro odkaz na objekt, který je odeslán do jiného procesu.
Objekt poskytovaný knihovnou DLL v procesu, která podporuje pouze volné vlákno, je objekt s volným vláknem. Implementuje vlastní synchronizaci a může k ní přistupovat více klientských vláken současně. Tento server nezařazuje rozhraní mezi vlákna, takže může být vytvořen a používán přímo (bez proxy) pouze vícevláknovými jednotkami v klientovi. Jednovláknové apartmány, které ho vytvářejí, k němu budou přistupovat prostřednictvím proxy serveru.