Descrizioni e funzionamento dei modelli di threading OLE

Questo articolo descrive i modelli di threading OLE.

Versione originale del prodotto: Modelli di threading OLE
Numero KB originale: 150777

Riepilogo

Gli oggetti COM possono essere usati in più thread di un processo. I termini "Single-threaded Apartment" (STA) e "Multi-threaded Apartment" (MTA) vengono usati per creare un framework concettuale per descrivere la relazione tra oggetti e thread, le relazioni di concorrenza tra gli oggetti, i mezzi con cui le chiamate al metodo vengono recapitate a un oggetto e le regole per il passaggio di puntatori di interfaccia tra thread. I componenti e i relativi client scelgono tra i due modelli di apartment attualmente supportati da COM:

  1. Modello Apartment a thread singolo (STA): uno o più thread in un processo usano COM e le chiamate agli oggetti COM vengono sincronizzate da COM. Viene eseguito il marshalling delle interfacce tra thread. Un caso degenerato del modello apartment a thread singolo, in cui un solo thread in un determinato processo usa COM, viene chiamato modello a threading singolo. In precedenza si è talvolta definito il modello STA semplicemente come "modello di appartamento".

  2. Modello Apartment multithreading (MTA): uno o più thread usano COM e le chiamate agli oggetti COM associati all'MTA vengono eseguite direttamente da tutti i thread associati all'MTA senza alcuna interposizione di codice di sistema tra chiamante e oggetto. Poiché più client simultanei possono chiamare oggetti più o meno contemporaneamente (contemporaneamente nei sistemi multiprocessore), gli oggetti devono sincronizzare autonomamente il proprio stato interno. Le interfacce non vengono sottoposte a marshalling tra thread. In precedenza questo modello è stato talvolta definito "modello a thread libero".

  3. Sia il modello STA che il modello MTA possono essere usati nello stesso processo. Questo processo viene talvolta definito "modello misto".

L'MTA è stato introdotto in NT 4.0 ed è disponibile in Windows 95 con DCOM95. Il modello STA esiste in Windows NT 3.51 e Windows 95, nonché in NT 4.0 e Windows 95 con DCOM95.

Panoramica

I modelli di threading in COM forniscono il meccanismo per il funzionamento di componenti che usano architetture di threading diverse. Forniscono anche servizi di sincronizzazione ai componenti che li richiedono. Ad esempio, un oggetto specifico può essere progettato per essere chiamato solo da un singolo thread e non può sincronizzare le chiamate simultanee dai client. Se tale oggetto viene chiamato contemporaneamente da più thread, si arresta in modo anomalo o causa errori. COM fornisce i meccanismi per gestire questa interoperabilità delle architetture di threading.

Anche i componenti in grado di riconoscere i thread spesso necessitano di servizi di sincronizzazione. Ad esempio, i componenti che hanno un'interfaccia utente grafica (GUI), ad esempio controlli OLE/ActiveX, incorporamenti attivi sul posto e documenti ActiveX, richiedono la sincronizzazione e la serializzazione di chiamate COM e messaggi di finestra. COM fornisce questi servizi di sincronizzazione in modo che questi componenti possano essere scritti senza codice di sincronizzazione complesso.

Un "appartamento" ha diversi aspetti correlati. In primo luogo, si tratta di un costrutto logico per considerare la concorrenza, ad esempio il modo in cui i thread sono correlati a un set di oggetti COM. In secondo luogo, si tratta di un set di regole che i programmatori devono rispettare per ricevere il comportamento di concorrenza previsto dall'ambiente COM. Infine, è il codice fornito dal sistema che consente ai programmatori di gestire la concorrenza dei thread rispetto agli oggetti COM.

Il termine "apartment" deriva da una metafora in cui un processo viene concepito come entità discreta, ad esempio un "edificio" suddiviso in un insieme di "locali" correlati ma diversi chiamati "appartamenti". Un apartment è un "contenitore logico" che crea un'associazione tra oggetti e, in alcuni casi, thread. I thread non sono appartamenti, anche se può essere presente un singolo thread associato logicamente a un apartment nel modello STA. Gli oggetti non sono appartamenti, anche se ogni oggetto è associato a un solo appartamento. Ma gli appartamenti non sono solo un costrutto logico; le regole descrivono il comportamento del sistema COM. Se le regole dei modelli apartment non vengono seguite, gli oggetti COM non funzioneranno correttamente.

Più dettagli

Un apartment a thread singolo (STA) è un set di oggetti COM associati a un determinato thread. Questi oggetti vengono associati all'apartment tramite la creazione da parte del thread o, più precisamente, la prima esposizione al sistema COM (in genere tramite marshalling) nel thread. AN STA è considerato un luogo in cui un oggetto o un proxy "vive". Se l'oggetto o il proxy deve essere accessibile da un altro apartment (nello stesso processo o in un processo diverso), è necessario effettuare il marshalling del puntatore dell'interfaccia a tale apartment in cui viene creato un nuovo proxy. Se vengono seguite le regole del modello apartment, non sono consentite chiamate dirette da altri thread nello stesso processo su tale oggetto; che viola la regola che tutti gli oggetti all'interno di un determinato apartment vengono eseguiti su un singolo thread. La regola esiste perché la maggior parte del codice in esecuzione in un'istanza STA non funzionerà correttamente se viene eseguita su thread aggiuntivi.

Il thread associato a un sta deve chiamare CoInitialize o CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) e deve recuperare e inviare messaggi della finestra affinché gli oggetti associati ricevano le chiamate in ingresso. COM invia e sincronizza le chiamate agli oggetti in un'istanza sta usando i messaggi della finestra, come descritto più avanti in questo articolo.

Il "main STA" è il thread che chiama CoInitialize o CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) prima all'interno di un determinato processo. L'sta principale di un processo deve rimanere attivo fino al completamento di tutto il lavoro COM, perché alcuni oggetti in-proc vengono sempre caricati nell'istanza sta principale, come descritto più avanti in questo articolo.

Windows NT 4.0 e DCOM95 introducono un nuovo tipo di apartment denominato apartment multithreading (MTA). Un MTA è un set di oggetti COM associati a un set di thread nel processo in modo che qualsiasi thread possa chiamare direttamente qualsiasi implementazione dell'oggetto senza l'interposizione del codice di sistema. I puntatori a interfaccia a qualsiasi oggetto nell'MTA possono essere passati tra i thread associati all'MTA senza dover essere sottoposti a marshalling. Tutti i thread nel processo che chiamano CoInitializeEx(NULL, COINIT_MULTITHREADED) sono associati all'MTA. A differenza del codice STA descritto in precedenza, i thread in un MTA non devono recuperare e inviare messaggi della finestra affinché gli oggetti associati ricevano chiamate in ingresso. COM non sincronizza le chiamate agli oggetti in un MTA. Gli oggetti in un MTA devono proteggere il proprio stato interno dal danneggiamento tramite l'interazione di più thread simultanei e non possono fare ipotesi sul contenuto di Thread-Local archiviazione rimanente costante tra chiamate di metodo diverse.

Un processo può avere un numero qualsiasi di MTA, ma al massimo può avere un MTA. L'MTA è costituito da uno o più thread. Le autorità di sicurezza hanno un thread ciascuno. Un thread appartiene, al massimo, a un appartamento. Gli oggetti appartengono a un solo appartamento. I puntatori a interfaccia devono essere sempre sottoposti a marshalling tra gli appartamenti,anche se il risultato del marshalling può essere un puntatore diretto anziché un proxy. Vedere le informazioni seguenti su CoCreateFreeThreadedMarshaler.

Un processo sceglie uno dei modelli di threading forniti da COM. Un processo del modello STA ha uno o più token di sicurezza e non dispone di un MTA. Un processo del modello MTA ha un'autenticazione a più fattori con uno o più thread e non dispone di alcun certificato di sicurezza. Un processo di modello misto ha un MTA e un numero qualsiasi di MTA.

Modello apartment a thread singolo

Il thread di un sta deve chiamare CoInitialize o CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) e deve recuperare e inviare messaggi della finestra perché COM usa i messaggi della finestra per sincronizzare e inviare il recapito delle chiamate a un oggetto in questo modello. Per altre informazioni, vedere la sezione RIFERIMENTI riportata di seguito.

Server che supporta il modello STA:

Nel modello STA le chiamate a un oggetto vengono sincronizzate da COM nello stesso modo in cui vengono sincronizzati i messaggi della finestra inviati a una finestra. Le chiamate vengono recapitate tramite messaggi di finestra al thread che ha creato l'oggetto. Di conseguenza, il thread dell'oggetto deve chiamare Get/PeekMessage e DispatchMessage ricevere chiamate. COM crea una finestra nascosta associata a ogni sta. Una chiamata a un oggetto dall'esterno di STA viene trasferita dal runtime COM al thread dell'oggetto usando un messaggio di finestra inviato a questa finestra nascosta. Quando il thread associato all'sta dell'oggetto recupera e invia il messaggio, la procedura della finestra per la finestra nascosta, implementata anche da COM, la riceve. La routine finestra viene usata dal runtime COM per "associare" il thread associato a STA perché il runtime COM si trova su entrambi i lati della chiamata dal thread di proprietà di COM al thread di STA. Il runtime COM (ora in esecuzione nel thread di STA) chiama "up" tramite uno stub fornito da COM nel metodo di interfaccia corrispondente dell'oggetto. Il percorso di esecuzione che restituisce dalla chiamata al metodo inverte la chiamata "up"; la chiamata restituisce lo stub e il runtime COM, che restituisce il controllo al thread di runtime COM tramite un messaggio di finestra, che quindi restituisce tramite il canale COM al chiamante originale.

Quando più client chiamano un oggetto STA, le chiamate vengono automaticamente accodate nella coda dei messaggi tramite il trasferimento del meccanismo di controllo usato in STA. L'oggetto riceve una chiamata ogni volta che sta recuperando e inviando messaggi. Poiché le chiamate vengono sincronizzate da COM in questo modo e poiché le chiamate vengono sempre recapitate nel thread singolo associato al valore STA dell'oggetto, le implementazioni dell'interfaccia dell'oggetto non devono fornire la sincronizzazione.

Nota

L'oggetto può essere reimmesso se un'implementazione del metodo di interfaccia recupera e invia messaggi durante l'elaborazione di una chiamata al metodo, causando il recapito di un'altra chiamata all'oggetto da parte dello stesso sta. Un modo comune in cui ciò si verifica è se un oggetto STA esegue una chiamata out-going (cross-apartment/cross-process) tramite COM. Questo è identico al modo in cui una routine finestra può essere immessa di nuovo se recupera e invia messaggi durante l'elaborazione di un messaggio. COM non impedisce il re-ingresso nello stesso thread, ma impedisce l'esecuzione simultanea. Fornisce anche un mezzo per gestire la reentrancy correlata a COM. Per altre informazioni, vedere la sezione RIFERIMENTI riportata di seguito. L'oggetto non viene reimmesso se le implementazioni del metodo non chiamano il relativo apartment o recuperano e inviano in altro modo i messaggi.

Responsabilità del client nel modello STA:

Il codice client in esecuzione in un processo e/o thread che usa il modello STA deve effettuare il marshalling delle interfacce di un oggetto tra gli appartamenti tramite CoMarshalInterThreadInterfaceInStream e CoGetInterfaceAndReleaseStream. Ad esempio, se Apartment 1 nel client ha un puntatore a interfaccia e Apartment 2 ne richiede l'uso, Apartment 1 deve effettuare il marshalling dell'interfaccia usando CoMarshalInterThreadInterfaceInStream. L'oggetto flusso restituito da questa funzione è thread-safe e il relativo puntatore a interfaccia deve essere archiviato in una variabile di memoria diretta accessibile da Apartment 2. Apartment 2 deve passare questa interfaccia di flusso a CoGetInterfaceAndReleaseStream per annullare ilmarshal dell'interfaccia sull'oggetto sottostante e recuperare un puntatore a un proxy tramite il quale può accedere all'oggetto.

L'apartment principale di un determinato processo deve rimanere attivo fino a quando il client non ha completato tutte le operazioni COM perché alcuni oggetti in-proc vengono caricati nell'apartment principale. Altre informazioni sono riportate di seguito.

Modello apartment multithreading

Un MTA è l'insieme di oggetti creati o esposti da tutti i thread nel processo che hanno chiamato CoInitializeEx(NULL, COINIT_MULTITHREADED).

Nota

Le implementazioni correnti di COM consentono a un thread che non inizializza in modo esplicito COM di far parte dell'MTA. Un thread che non inizializza COM fa parte dell'MTA solo se inizia a usare COM dopo che almeno un altro thread nel processo ha chiamato CoInitializeEx(NULL, COINIT_MULTITHREADED)in precedenza . (È anche possibile che COM stesso abbia inizializzato l'MTA quando nessun thread client lo ha fatto in modo esplicito; ad esempio, un thread associato a una chiamata CoGetClassObject/CoCreateInstance[Ex] STA in un CLSID contrassegnato come "ThreadingModel=Free" e COM crea in modo implicito un MTA in cui viene caricato l'oggetto classe. Vedere le informazioni sull'interoperabilità del modello di threading riportate di seguito.

Si tratta tuttavia di una configurazione che potrebbe causare problemi, ad esempio violazioni di accesso, in determinate circostanze. Pertanto, è consigliabile che ogni thread che deve eseguire operazioni COM inizializzare COM chiamando CoInitializeEx e quindi, al termine del lavoro COM, chiamare CoUninitialize. Il costo dell'inizializzazione "inutilmente" di un MTA è minimo.

I thread MTA non devono recuperare e inviare messaggi perché COM non usa messaggi di finestra in questo modello per recapitare chiamate a un oggetto.

  • Server che supporta il modello MTA:

    Nel modello MTA le chiamate a un oggetto non vengono sincronizzate da COM. Più client possono chiamare simultaneamente un oggetto che supporta questo modello in thread diversi e l'oggetto deve fornire la sincronizzazione nelle implementazioni di interfaccia/metodo usando oggetti di sincronizzazione come eventi, mutex, semafori e così via. Gli oggetti MTA possono ricevere chiamate simultanee da più client out-of-process tramite un pool di thread creati da COM appartenenti al processo dell'oggetto. Gli oggetti MTA possono ricevere chiamate simultanee da più client in-process su più thread associati all'MTA.

  • Responsabilità del client nel modello MTA:

    Il codice client in esecuzione in un processo e/o thread che usa il modello MTA non deve effettuare il marshalling dei puntatori di interfaccia di un oggetto tra se stesso e altri thread MTA. Un thread MTA può invece usare un puntatore a interfaccia ottenuto da un altro thread MTA come puntatore diretto alla memoria. Quando un thread client effettua una chiamata a un oggetto out-of-process, viene sospeso fino al completamento della chiamata. Le chiamate possono arrivare su oggetti associati all'MTA mentre tutti i thread creati dall'applicazione associati all'MTA vengono bloccati nelle chiamate in uscita. In questo caso e in generale, le chiamate in ingresso vengono recapitate sui thread forniti dal runtime COM. I filtri dei messaggi (IMessageFilter) non sono disponibili per l'uso nel modello MTA.

Modelli di threading misto

Un processo che supporta il modello di threading misto userà un MTA e uno o più token di sicurezza. I puntatori di interfaccia devono essere sottoposti a marshalling tra tutti gli appartamenti, ma possono essere usati senza effettuare il marshalling all'interno dell'MTA. Le chiamate agli oggetti in un'istanza sta di vengono sincronizzate da COM per l'esecuzione su un solo thread, mentre le chiamate agli oggetti nell'MTA non lo sono. Tuttavia, le chiamate da sta a MTA in genere passano attraverso il codice fornito dal sistema e passano dal thread STA a un thread MTA prima di essere recapitate all'oggetto.

Nota

Per informazioni sui casi in cui è possibile usare i puntatori diretti e su CoCreateFreeThreadedMarshaler() come un thread STA può chiamare direttamente in un oggetto associato all'MTA e viceversa, da più appartamenti, vedere la documentazione dell'SDK e la discussione sull'API riportata di seguito.

Scelta dei modelli di threading

Un componente può scegliere di supportare il modello STA, il modello MTA o una combinazione dei due usando il modello di threading misto. Ad esempio, un oggetto che esegue operazioni di I/O estese può scegliere di supportare MTA per fornire la massima risposta ai client consentendo l'esecuzione di chiamate di interfaccia durante la latenza di I/O. In alternativa, un oggetto che interagisce con l'utente sceglie quasi sempre di supportare STA per sincronizzare le chiamate COM in ingresso con le relative operazioni GUI. Il supporto del modello STA è più semplice perché COM fornisce la sincronizzazione. Il supporto del modello MTA è più difficile perché l'oggetto deve implementare la sincronizzazione, ma la risposta ai client è migliore perché la sincronizzazione viene usata per sezioni di codice più piccole, anziché per l'intera chiamata di interfaccia fornita da COM.

Il modello STA viene usato anche da Microsoft Transaction Server (MTS, in precedenza denominato "Viper") e pertanto gli oggetti basati su DLL che pianificano l'esecuzione all'interno dell'ambiente MTS devono usare il modello STA. Gli oggetti implementati per il modello MTA funzionano normalmente correttamente in un ambiente MTS. Tuttavia, verranno eseguiti in modo meno efficiente perché useranno primitive di sincronizzazione dei thread non necessarie.

Contrassegno del modello di threading supportato dei server In-Proc

Un thread usa il modello MTA se chiama CoInitializeEx(NULL, COINIT_MULTITHREADED) o usa COM senza inizializzarlo. Un thread usa il modello STA se chiama CoInitialize o CoInitializeEx(NULL, COINIT_APARTMENTTHREADED).

Le CoInitialize API forniscono il controllo apartment per il codice client e per gli oggetti inclusi nel pacchetto. EXE, perché il codice di avvio del runtime COM può inizializzare COM nel modo desiderato.

Tuttavia, un server COM in-proc (basato su DLL) non chiama CoInitialize/CoInitializeEx perché tali API saranno state chiamate al momento del caricamento del server DLL. Pertanto, un server DLL deve usare il Registro di sistema per informare COM del modello di threading supportato in modo che COM possa garantire che il sistema funzioni in modo compatibile con esso. A questo scopo viene usato un valore denominato della chiave CLSID\InprocServer32 del componente, ThreadingModel come indicato di seguito:

  • ThreadingModel valore non presente: supporta il modello a threading singolo.
  • ThreadingModel=Apartment: supporta il modello STA.
  • ThreadingModel=Both: supporta il modello STA e MTA.
  • ThreadingModel=Free: supporta solo MTA.

Nota

ThreadingModel è un valore denominato, non una sottochiave di InprocServer32 come erroneamente documentato in alcune versioni precedenti della documentazione di Win32.

I modelli di threading dei server in-proc sono illustrati più avanti in questo articolo. Se un server in-proc fornisce molti tipi di oggetti (ognuno con il proprio CLSID univoco), ogni tipo può avere un valore diverso ThreadingModel . In altre parole, il modello di threading è per CLSID, non per pacchetto di codice/DLL. Tuttavia, i punti di ingresso dell'API necessari per "bootstrap" ed eseguire query su tutti i server in-proc (DLLGetClassObject(), DLLCanUnloadNow()) devono essere thread-safe per qualsiasi server in-proc che supporti più thread ,ovvero un ThreadingModel valore Apartment, Both o Free.

Come indicato in precedenza, i server out-of-process non si contrassegnano usando il valore ThreadingModel. CoInitialize Usano invece o CoInitializeEx. I server basati su DLL che prevedono l'esecuzione out-of-process usando la funzionalità "surrogato" di COM (ad esempio il DLLHOST.EXE surrogato fornito dal sistema) seguono le regole per i server basati su DLL; in questo caso non vi sono considerazioni particolari.

Quando il client e l'oggetto usano modelli di threading diversi

L'interazione tra un client e un oggetto out-of-process è diretta anche quando vengono usati modelli di threading diversi perché il client e l'oggetto si trovano in processi diversi e COM è coinvolto nel passaggio di chiamate dal client all'oggetto. Poiché COM è interposto tra il client e il server, fornisce il codice per l'interoperabilità dei modelli di threading. Ad esempio, se un oggetto STA viene chiamato contemporaneamente da più client STA o MTA, COM sincronizza le chiamate inserendo i messaggi della finestra corrispondenti nella coda dei messaggi del server. Il valore STA dell'oggetto riceve una chiamata ogni volta che recupera e invia messaggi. Tutte le combinazioni di interoperabilità del modello di threading sono consentite e completamente supportate tra i client e gli oggetti out-of-process.

L'interazione tra un client e un oggetto in-proc che usa modelli di threading diversi è più complessa. Anche se il server è in-proc, COM deve interposersi tra il client e l'oggetto in alcuni casi. Ad esempio, un oggetto in-proc progettato per supportare il modello STA può essere chiamato contemporaneamente da più thread di un client. COM non può consentire ai thread client di accedere direttamente all'interfaccia dell'oggetto perché l'oggetto non è progettato per tale accesso simultaneo. Com deve invece assicurarsi che le chiamate siano sincronizzate ed eseguite solo dal thread associato all'oggetto STA che "contiene". Nonostante la complessità aggiuntiva, tutte le combinazioni di interoperabilità threading-model sono consentite tra i client e gli oggetti in-proc.

Modelli di threading nei server out-of-process (basati su EXE)

Di seguito sono riportate tre categorie di server out-of-process, ognuna delle quali può essere usata da qualsiasi client COM indipendentemente dal modello di threading usato da tale client:

  1. Server di modello STA:

    Il server esegue il lavoro COM in uno o più token di sicurezza. Le chiamate in ingresso vengono sincronizzate da COM e recapitate dal thread associato all'sta in cui è stato creato l'oggetto. Le chiamate al metodo class-factory vengono recapitate dal thread associato all'istanza sta che ha registrato la class factory. L'oggetto e la class factory non devono implementare la sincronizzazione. Tuttavia, l'implementatore deve sincronizzare l'accesso a tutte le variabili globali usate da più autorità di sicurezza. Il server deve usare CoMarshalInterThreadInterfaceInStream e CoGetInterfaceAndReleaseStream per effettuare il marshalling dei puntatori di interfaccia, possibilmente da altri server, tra le istanze di token di sicurezza. Facoltativamente, il server può implementare IMessageFilter in ogni sta per controllare gli aspetti del recapito delle chiamate da parte di COM. Un caso degenerato è il server modello a threading singolo che esegue il funzionamento COM in un'istanza sta.

  2. Server modello MTA:

    Il server esegue il lavoro COM in uno o più thread, tutti appartenenti all'MTA. Le chiamate non vengono sincronizzate da COM. COM crea un pool di thread nel processo server e una chiamata client viene recapitata da uno di questi thread. I thread non devono recuperare e inviare messaggi. L'oggetto e la class factory devono implementare la sincronizzazione. Il server non deve effettuare il marshalling dei puntatori di interfaccia tra thread.

  3. Server modello misto:

    Per altre informazioni, vedere la sezione in questo articolo intitolata "Modello di threading misto".

Modelli di threading nei client

Esistono tre categorie di client:

  1. Client del modello STA:

    Il client esegue il lavoro COM in thread associati a uno o più token di sicurezza. Il client deve usare CoMarshalInterThreadInterfaceInStream e CoGetInterfaceAndReleaseStream per effettuare il marshalling dei puntatori di interfaccia tra le autorità di sicurezza. Un caso degenerato è il client del modello a threading singolo che esegue il funzionamento COM in un'istanza sta. Il thread del client entra in un ciclo di messaggi fornito da COM quando esegue una chiamata in uscita. Il client può usare IMessageFilter per gestire i callback e l'elaborazione dei messaggi della finestra durante l'attesa di chiamate in uscita e altri problemi di concorrenza.

  2. Client del modello MTA:

    Il client esegue il lavoro COM in uno o più thread, tutti appartenenti all'MTA. Il client non deve effettuare il marshalling dei puntatori di interfaccia tra i relativi thread. Il client non può usare IMessageFilter. I thread del client vengono sospesi quando effettuano una chiamata COM a un oggetto out-of-process e riprendono quando la chiamata viene restituita. Le chiamate in arrivo arrivano su thread gestiti e creati da COM.

  3. Client modello misto:

    Per altre informazioni, vedere la sezione in questo articolo intitolata "Modello di threading misto".

Modelli di threading nei server in-proc (basati su DLL)

Esistono quattro categorie di server in-proc, ognuna delle quali può essere usata da qualsiasi client COM indipendentemente dal modello di threading usato da tale client. Tuttavia, i server in-proc devono fornire codice di marshalling per qualsiasi interfaccia personalizzata (non definita dal sistema) implementata se devono supportare l'interoperabilità del modello di threading perché ciò richiede in genere il marshalling COM dell'interfaccia tra gli appartamenti client. Le quattro categorie sono:

  1. Server in-proc che supporta il threading singolo ("main" STA)- nessun ThreadingModel valore:

    Un oggetto fornito da questo server prevede di essere accessibile dallo stesso client STA con cui è stato creato. Inoltre, il server prevede che tutti i punti di ingresso, ad DllGetClassObject esempio e DllCanUnloadNow, e i dati globali siano accessibili dallo stesso thread (quello associato all'sta principale). I server esistenti prima dell'introduzione del multithreading in COM rientrano in questa categoria. Questi server non sono progettati per l'accesso da più thread, pertanto COM crea tutti gli oggetti forniti dal server nell'sta principale del processo e le chiamate agli oggetti vengono recapitate dal thread associato all'sta principale. Altri appartamenti client ottengono l'accesso all'oggetto tramite proxy. Le chiamate dagli altri appartamenti passano dal proxy allo stub nello sta principale (marshalling tra thread) e quindi all'oggetto . Questo marshalling consente a COM di sincronizzare le chiamate all'oggetto e le chiamate vengono recapitate dall'AGENTE in cui è stato creato l'oggetto. Il marshalling tra thread è lento rispetto alle chiamate dirette, pertanto è consigliabile riscrivere questi server per supportare più istanze di token di sicurezza (categoria 2).

  2. Server in-proc che supporta il modello apartment a thread singolo (più token di sicurezza) contrassegnato con ThreadingModel=Apartment:

    Un oggetto fornito da questo server prevede di essere accessibile dallo stesso client STA con cui è stato creato. Pertanto, è simile a un oggetto fornito da un server in-proc a thread singolo. Tuttavia, gli oggetti forniti da questo server possono essere creati in più token di sicurezza del processo, quindi il server deve progettare i relativi punti di ingresso, ad DllGetClassObject esempio e DllCanUnloadNow, e i dati globali per l'uso multithreading. Se, ad esempio, due istanze di token di sicurezza di un processo creano due istanze dell'oggetto in-proc contemporaneamente, DllGetClassObject possono essere chiamate simultaneamente da entrambe le istanze di token di sicurezza. Analogamente, DllCanUnloadNow è necessario scrivere in modo che il server sia protetto dal caricamento mentre il codice è ancora in esecuzione nel server.

    Se il server fornisce una sola istanza della class factory per creare tutti gli oggetti, l'implementazione della class factory deve essere progettata anche per l'uso multithreading perché è accessibile da più token di sicurezza client. Se il server crea una nuova istanza della class factory ogni volta DllGetClassObject che viene chiamata, la class factory non deve essere thread-safe. Tuttavia, l'implementatore deve sincronizzare l'accesso a qualsiasi variabile globale.

    Gli oggetti COM creati dalla class factory non devono essere thread-safe. Tuttavia, l'accesso alle variabili globali deve essere sincronizzato dall'implementatore. Una volta creato da un thread, l'accesso all'oggetto viene sempre eseguito tramite tale thread e tutte le chiamate all'oggetto vengono sincronizzate da COM. Gli appartamenti client diversi da quelli in cui è stato creato l'oggetto devono accedere all'oggetto tramite proxy. Questi proxy vengono creati quando il client effettua il marshalling dell'interfaccia tra i relativi appartamenti.

    Qualsiasi client che crea un oggetto STA tramite la relativa class factory ottiene un puntatore diretto all'oggetto . Questa operazione è diversa dagli oggetti in-proc a thread singolo, in cui solo il valore STA principale del client ottiene un puntatore diretto all'oggetto e tutte le altre autorità di sicurezza che creano l'oggetto ottengono l'accesso all'oggetto tramite un proxy. Poiché il marshalling tra thread è lento rispetto alla chiamata diretta, è possibile migliorare la velocità modificando un server in-proc a thread singolo per supportare più istanze di token di sicurezza.

  3. Server in-proc che supporta solo MTA, contrassegnato con ThreadingModel=Free:

    Un oggetto fornito da questo server è sicuro solo per l'MTA. Implementa la propria sincronizzazione ed è accessibile da più thread client contemporaneamente. Questo server potrebbe avere un comportamento incompatibile con il modello STA. Ad esempio, tramite l'uso della coda di messaggi di Windows in modo da interromperne la distribuzione. Inoltre, contrassegnando il modello di threading dell'oggetto come "Free", l'implementatore dell'oggetto indica quanto segue: questo oggetto può essere chiamato da qualsiasi thread client, ma questo oggetto può anche passare direttamente i puntatori di interfaccia (senza marshalling) a tutti i thread creati e questi thread possono effettuare chiamate tramite questi puntatori. Pertanto, se il client passa un puntatore di interfaccia a un oggetto implementato dal client (ad esempio un sink) a questo oggetto, può scegliere di richiamare tramite questo puntatore a interfaccia da qualsiasi thread creato. Se il client è un valore STA, una chiamata diretta da un thread, diversa dal thread che ha creato l'oggetto sink, verrà eseguita in modo errato (come illustrato in 2 precedenti). Com garantisce quindi sempre che i client nei thread associati a un'istanza stante ottengano l'accesso a questo tipo di oggetto in-proc solo tramite un proxy. Inoltre, questi oggetti non devono essere aggregati con il gestore di marshalling a thread libero perché ciò consente loro di essere eseguiti direttamente nei thread STA.

  4. Server in-proc che supporta il modello apartment e il free-threading, contrassegnato con ThreadingModel=Both:

    Un oggetto fornito da questo server implementa la propria sincronizzazione ed è accessibile contemporaneamente da più appartamenti client. Inoltre, questo oggetto viene creato e usato direttamente, anziché tramite un proxy, nelle istanze del servizio token di sicurezza o nell'MTA di un processo client. Poiché questo oggetto viene usato direttamente nelle istanze del servizio token di sicurezza, il server deve effettuare il marshalling di interfacce di oggetti, possibilmente da altri server, tra thread, in modo da garantire l'accesso a qualsiasi oggetto in modo appropriato per il threading. Inoltre, contrassegnando il modello di threading dell'oggetto come "Entrambi", l'implementatore dell'oggetto indica quanto segue: questo oggetto può essere chiamato da qualsiasi thread client, ma tutti i callback da questo oggetto al client verranno eseguiti solo nell'apartment in cui l'oggetto ha ricevuto il puntatore a interfaccia all'oggetto callback. COM consente di creare un oggetto di questo tipo direttamente in un'istanza sta e in un'istanza MTA del processo client.

    Poiché qualsiasi apartment che crea un oggetto di questo tipo ottiene sempre un puntatore diretto anziché un puntatore proxy, ThreadingModel "Both" gli oggetti offrono miglioramenti delle prestazioni rispetto agli ThreadingModel "Free" oggetti caricati in un oggetto STA.

    Poiché un ThreadingModel "Both" oggetto è progettato anche per l'accesso MTA (è thread-safe internamente), può velocizzare le prestazioni aggregandosi con il gestore di marshalling fornito da CoCreateFreeThreadedMarshaler. Questo oggetto fornito dal sistema viene aggregato in a tutti gli oggetti chiamanti e i marshalling personalizzati indirizzano i puntatori all'oggetto in tutti gli appartamenti del processo. I client in qualsiasi apartment, sia sta che MTA, possono quindi accedere direttamente all'oggetto anziché tramite un proxy. Ad esempio, un client del modello STA crea l'oggetto in-proc in STA1 e esegue il marshalling dell'oggetto su STA2. Se l'oggetto non si aggrega con il gestore di marshalling a thread libero, STA2 ottiene l'accesso all'oggetto tramite un proxy. In caso affermativo, il gestore di marshalling a thread libero fornisce a STA2 un puntatore diretto all'oggetto

    Nota

    Quando si esegue l'aggregazione con il gestore di marshalling a thread libero, è necessario prestare attenzione. Si supponga, ad esempio, che un oggetto contrassegnato come ThreadingModel "Both" (e che si aggrega anche con il gestore di marshalling a thread libero) abbia un membro dati che è un puntatore a interfaccia a un altro oggetto il cui ThreadingModel oggetto è "Apartment". Si supponga quindi che un oggetto STA crei il primo oggetto e durante la creazione, il primo oggetto crea il secondo oggetto. In base alle regole descritte in precedenza, il primo oggetto contiene ora un puntatore diretto al secondo oggetto. Si supponga ora che l'agente di configurazione di azure eservi il marshalling del puntatore dell'interfaccia al primo oggetto in un altro apartment. Poiché il primo oggetto viene aggregato con il gestore di marshalling a thread libero, al secondo apartment viene assegnato un puntatore diretto al primo oggetto. Se il secondo apartment chiama quindi tramite questo puntatore e se questa chiamata fa sì che il primo oggetto chiami tramite il puntatore a interfaccia al secondo oggetto, si è verificato un errore, perché il secondo oggetto non è destinato a essere chiamato direttamente dal secondo apartment. Se il primo oggetto contiene un puntatore a un proxy al secondo oggetto anziché a un puntatore diretto, verrà generato un errore diverso. I proxy di sistema sono anche oggetti COM associati a un solo apartment. Tengono traccia del loro appartamento per evitare alcune circolarità. Un oggetto che chiama su un proxy associato a un apartment diverso dal thread in cui è in esecuzione l'oggetto riceverà il RPC_E_WRONG_THREAD restituito dal proxy e la chiamata avrà esito negativo.

Interoperabilità del modello di threading tra client e oggetti in-process

Tutte le combinazioni di interoperabilità threading-model sono consentite tra i client e gli oggetti in-process.

COM consente a tutti i client del modello STA di eseguire l'interoperabilità con oggetti in-proc a threading singolo creando e accedendo all'oggetto nell'istanza STA principale del client e eseguendo il marshalling al client STA che ha chiamato CoCreateInstance[Ex].

Se un MTA in un client crea un modello STA nel server in-proc, COM avvia un valore STA "host" nel client. Questo sta host crea l'oggetto e viene eseguito il marshalling del puntatore dell'interfaccia all'MTA. Analogamente, quando un'istanza sta crea un server MTA in-proc, COM avvia un MTA host in cui viene creato l'oggetto e ne viene eseguito il marshalling al server STA. L'interoperabilità tra il modello a threading singolo e il modello MTA viene gestita in modo analogo perché il modello a threading singolo è solo un caso degenerato del modello STA.

Il codice di marshalling deve essere fornito per qualsiasi interfaccia personalizzata implementata da un server in-proc se vuole supportare l'interoperabilità che richiede com per effettuare il marshalling dell'interfaccia tra gli appartamenti client. Per altre informazioni, vedere la sezione RIFERIMENTI riportata di seguito.

Relazione tra il modello di threading e l'oggetto Class Factory restituito

Una definizione precisa dei server in-proc da caricare negli appartamenti è illustrata nei due passaggi seguenti:

  1. Se la DLL che contiene la classe server in-proc non è stata precedentemente caricata (mappata nello spazio degli indirizzi del processo) dal caricatore del sistema operativo, tale operazione viene eseguita e COM ottiene l'indirizzo della DLLGetClassObject funzione esportata dalla DLL. Se la DLL è stata caricata in precedenza da un thread associato a qualsiasi apartment, questa fase viene ignorata.

  2. COM usa il thread (o, nel caso dell'MTA, uno dei thread) associato all'apartment di caricamento per chiamare la DllGetClassObject funzione esportata dalla DLL che richiede il CLSID della classe necessaria. L'oggetto factory restituito viene quindi utilizzato per creare istanze di oggetti della classe .

    Il secondo passaggio (la chiamata di da parte di DllGetClassObject COM) si verifica ogni volta che un client chiama CoGetClassObject/CoCreateIntance[Ex], anche dall'interno dello stesso apartment. In altre parole, DllGetClassObject può essere chiamato molte volte da un thread associato allo stesso apartment. Tutto dipende dal numero di client in tale apartment che tentano di ottenere l'accesso a un oggetto class factory per tale classe.

Gli autori di implementazioni di classi e, in definitiva, l'autore del pacchetto DLL di un determinato set di classi hanno la completa libertà di decidere quale oggetto factory restituire in risposta alla chiamata di DllGetClassObject funzione. L'autore del pacchetto DLL ha l'ultima parola perché il codice "dietro" il singolo punto di ingresso a livello DllGetClassObject() di DLL è ciò che ha il primo e potenzialmente finale diritto di decidere cosa fare. Le tre possibilità tipiche sono:

  1. DllGetClassObject restituisce un oggetto factory di classe univoco per ogni thread chiamante ,ovvero un oggetto factory di classe per sta e potenzialmente più class factory all'interno dell'MTA.

  2. DllGetClassObject restituisce sempre lo stesso oggetto factory di classe, indipendentemente dall'identità del thread chiamante o dal tipo di apartment associato al thread chiamante.

  3. DllGetClassObject restituisce un oggetto factory di classe univoco per ogni apartment chiamante (uno per appartamento sia in STA che in MTA).

Esistono altre possibilità per la relazione tra le chiamate a DllGetClassObject e l'oggetto factory della classe restituito (ad esempio un nuovo oggetto factory di classe per ogni chiamata a DllGetClassObject), ma attualmente non sembrano essere utili.

Riepilogo dei modelli di threading di client e oggetti per i server In-Proc

La tabella seguente riepiloga l'interazione tra modelli di threading diversi quando un thread client chiama CoGetClassObject per la prima volta su una classe implementata come server in-proc.

Tipi di client/thread:

  • il client è in esecuzione in un thread associato a "main" STA (primo thread da chiamare CoInitialize o CoInitializeEx con COINIT_APARTMENTTHREADED flag)-call this STA0 (detto anche modello di threading singolo).
  • il client è in esecuzione in un thread associato a in qualsiasi altro sta [ASCII 150] chiamare questo STA*.
  • il client è in esecuzione in un thread associato a nell'MTA.

Tipi di server DLL:

  • Il server non dispone di alcuna ThreadingModel chiave, chiamata "Nessuna".
  • Il server è contrassegnato come "Apartment"-- chiama questo "Apt".
  • Il server è contrassegnato come "Gratuito".
  • Il server è contrassegnato come "Entrambi".

Quando si legge la tabella seguente, tenere presente la definizione precedente di "caricamento" di un server in un apartment.

Client         Server                 Result
STA0           None                   Direct access; server loaded into STA0  
STA*           None                   Proxy access; server loaded into STA0.  
MTA            None                   Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary;  
STA0           Apt                    Direct access; server loaded into STA0  
STA*           Apt                    Direct access; server loaded into STA*  
MTA            Apt                    Proxy access; server loaded into an STA created automatically by COM.
STA0           Free                   Proxy access; server is loaded into MTA MTA created automatically by COM if necessary.
STA*           Free                   Same as STA0->Free
MTA            Free                   Direct access
STA0           Both                   Direct access; server loaded into STA0
STA*           Both                   Direct access; server loaded into STA*
MTA            Both                   Direct access; server loaded into the MTA

Riferimenti

Documentazione dell'SDK sull'interfaccia CoRegisterMessageFilter() e IMessageFilter .