Descrizioni e operazioni 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 "Apartment a thread singolo" (STA) e "Apartment a thread multipli" (MTA) vengono usati per creare un framework concettuale per descrivere la relazione tra oggetti e thread, le relazioni di concorrenza tra gli oggetti, il mezzo tramite il quale le chiamate al metodo vengono recapitate a un oggetto e le regole per passare puntatori di interfaccia tra thread. I componenti e i relativi client scelgono tra i due modelli apartment seguenti attualmente supportati da COM:
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. Le interfacce vengono sottoposto a marshalling 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 thread singolo. In precedenza il modello STA veniva talvolta definito semplicemente "modello apartment".
Modello Apartment multithread (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 sovrapposizione del codice di sistema tra il chiamante e l'oggetto. Poiché più client simultanei possono chiamare oggetti più o meno simultaneamente (contemporaneamente nei sistemi multiprocessore), gli oggetti devono sincronizzare il proprio stato interno da soli. Le interfacce non vengono sottoposto a marshalling tra thread. In precedenza questo modello veniva talvolta definito "modello a thread libero".
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, oltre a NT 4.0 e Windows 95 con DCOM95.
Panoramica
I modelli di threading in COM forniscono il meccanismo per i componenti che usano architetture di threading diverse per lavorare insieme. Forniscono anche servizi di sincronizzazione ai componenti che li richiedono. Ad esempio, un particolare oggetto può essere progettato per essere chiamato solo da un singolo thread e potrebbe non sincronizzare le chiamate simultanee dai client. Se tale oggetto viene chiamato simultaneamente 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 riconoscimento del thread spesso richiedono servizi di sincronizzazione. Ad esempio, i componenti con 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 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 pensare alla concorrenza, ad esempio il modo in cui i thread sono correlati a un set di oggetti COM. In secondo luogo, è un set di regole che i programmatori devono rispettare per ricevere il comportamento di concorrenza previsto dall'ambiente COM. Infine, si tratta di 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 un'entità discreta, ad esempio un "edificio" suddiviso in un set 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 uno e a un solo appartamento. Ma gli appartamenti sono più di un semplice costrutto logico; le relative regole descrivono il comportamento del sistema COM. Se le regole dei modelli apartment non vengono seguite, gli oggetti COM non funzioneranno correttamente.
Altri dettagli
Un apartment a thread singolo (STA) è un set di oggetti COM associati a un thread specifico. Questi oggetti sono associati all'apartment creando dal thread o, più precisamente, essere esposti al sistema COM (in genere eseguendo il marshalling) sul thread. UN STA è considerato un luogo in cui un oggetto o un proxy "vive". Se è necessario accedere all'oggetto o al proxy 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; 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 sta non funzionerà correttamente se viene eseguita su thread aggiuntivi.
Il thread associato a un sta sta deve chiamare CoInitialize
o CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)
e deve recuperare e inviare i messaggi della finestra per gli oggetti associati per ricevere le chiamate in ingresso. COM invia e sincronizza le chiamate agli oggetti in un'istanza di sta usando messaggi di finestra, come descritto più avanti in questo articolo.
"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 a quando non viene completato tutto il lavoro COM perché alcuni oggetti in-proc vengono sempre caricati nella 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 qualsiasi implementazione dell'oggetto direttamente senza l'interposizione del codice di sistema. I puntatori di interfaccia a qualsiasi oggetto nell'MTA possono essere passati tra i thread associati all'MTA senza dover eseguire il marshalling. Tutti i thread nel processo che chiamano CoInitializeEx(NULL, COINIT_MULTITHREADED)
sono associati all'MTA. A differenza della sta descritta in precedenza, i thread in un MTA non devono recuperare e inviare messaggi della finestra per gli oggetti associati per ricevere le 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 dell'archiviazione locale thread rimanente tra diverse chiamate al metodo.
Un processo può avere un numero qualsiasi di stA, ma, al massimo, può avere un MTA. L'MTA è costituito da uno o più thread. Gli account del servizio token di sicurezza hanno un thread ciascuno. Un thread appartiene, al massimo, a un appartamento. Gli oggetti appartengono a uno e a un solo appartamento. I puntatori di interfaccia devono essere sempre sottoposto a marshalling tra 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 di modello STA ha uno o più stA e non dispone di un MTA. Un processo di modello MTA ha un MTA con uno o più thread e non dispone di alcun contratto di sicurezza. Un processo di modello misto ha un MTA e un numero qualsiasi di stA.
Modello apartment a thread singolo
Il thread di una sta sta deve chiamare CoInitialize
o CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)
e deve recuperare e inviare i messaggi della finestra perché COM usa messaggi finestra per sincronizzare e inviare il recapito delle chiamate a un oggetto in questo modello. Per altre informazioni, vedere la sezione REFERENCES 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 i messaggi di finestra inviati a una finestra vengono sincronizzati. 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 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 routine della finestra per la finestra nascosta, implementata anche da COM, la riceve. La procedura della 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à COM al thread sta. Il runtime COM (ora in esecuzione nel thread sta) chiama "up" tramite uno stub fornito da COM nel metodo di interfaccia corrispondente dell'oggetto. Il percorso di esecuzione restituito dalla chiamata al metodo inverte la chiamata "up" ; la chiamata torna allo stub e al runtime COM, che passa il controllo al thread di runtime COM tramite un messaggio di finestra, che quindi torna attraverso il canale COM al chiamante originale.
Quando più client chiamano un oggetto STA, le chiamate vengono accodate automaticamente nella coda dei messaggi dal trasferimento del meccanismo di controllo usato nella sta sta. L'oggetto riceve una chiamata ogni volta che sta recupera e invia messaggi. Poiché le chiamate vengono sincronizzate da COM in questo modo e poiché le chiamate vengono sempre recapitate sul singolo thread associato all'sta dell'oggetto, le implementazioni dell'interfaccia dell'oggetto non devono fornire la sincronizzazione.
Note
L'oggetto può essere nuovamente immesso 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 sta. Un modo comune in cui ciò si verifica è se un oggetto STA effettua una chiamata in uscita (cross-apartment/cross-process) tramite COM. Questo è identico al modo in cui una routine finestra può essere nuovamente immessa se recupera e invia messaggi durante l'elaborazione di un messaggio. COM non impedisce l'ingresso di nuovo sullo stesso thread, ma impedisce l'esecuzione simultanea. Fornisce anche un mezzo per gestire la reentrancy correlata a COM. Per altre informazioni, vedere la sezione REFERENCES riportata di seguito. L'oggetto non viene nuovamente immesso se le implementazioni del metodo non escono dal relativo apartment o recuperano e in altro modo recuperino i messaggi.
Responsabilità del client nel modello STA:
Il codice client in esecuzione in un processo e/o in un thread che usa il modello STA deve effettuare il marshalling delle interfacce di un oggetto tra appartamenti tramite CoMarshalInterThreadInterfaceInStream
e CoGetInterfaceAndReleaseStream
. Ad esempio, se Apartment 1 nel client ha un puntatore all'interfaccia e Apartment 2 richiede l'uso di esso, Apartment 1 deve effettuare il marshalling dell'interfaccia usando CoMarshalInterThreadInterfaceInStream
. L'oggetto flusso restituito da questa funzione è thread-safe e il relativo puntatore di interfaccia deve essere archiviato in una variabile di memoria diretta accessibile dall'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'appartamento principale di un determinato processo deve rimanere attivo fino a quando il cliente non ha completato tutto il lavoro COM perché alcuni oggetti in-proc vengono caricati nell'appartamento principale. Altre informazioni sono riportate di seguito.
Modello apartment multithreading
Un MTA è la raccolta di oggetti creati o esposti da tutti i thread nel processo che hanno chiamato CoInitializeEx(NULL, COINIT_MULTITHREADED)
.
Note
Le implementazioni correnti di COM consentono a un thread che non inizializza esplicitamente COM di far parte dell'MTA. Un thread che non inizializza COM fa parte dell'MTA solo se inizia a usare COM dopo almeno un altro thread nel processo ha precedentemente chiamato CoInitializeEx(NULL, COINIT_MULTITHREADED)
. (È anche possibile che COM stesso abbia inizializzato l'MTA quando non è stato eseguito in modo esplicito alcun thread client; 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 di seguito.
Tuttavia, si tratta di una configurazione che potrebbe causare problemi, ad esempio violazioni di accesso, in determinate circostanze. Pertanto, è consigliabile che ogni thread che deve eseguire l'inizializzazione COM di COM chiamando CoInitializeEx
e quindi, al completamento 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 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 in un 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 di 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, sospende 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 in caso di chiamate in uscita. In questo caso e in generale, le chiamate in ingresso vengono recapitate nei thread forniti dal runtime COM. I filtri 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ù stA. I puntatori di interfaccia devono essere sottoposto a marshalling tra tutti gli appartamenti, ma possono essere usati senza effettuare il marshalling all'interno dell'MTA. Le chiamate agli oggetti in una sta sono sincronizzate da COM per l'esecuzione su un solo thread mentre le chiamate agli oggetti nell'MTA non sono. Tuttavia, le chiamate da una sta a un MTA in genere passano attraverso il codice fornito dal sistema e passano dal thread STA a un thread MTA prima di essere recapitati all'oggetto.
Note
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 descrizione dell'API seguente.
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 delle 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, precedentemente 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 in un ambiente MTS. Tuttavia, verranno eseguiti in modo meno efficiente perché usano 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. EXEs, poiché 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 dal momento in cui il server DLL viene caricato. 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. Per questo scopo viene usato un valore denominato della chiave ThreadingModel
CLSID\InprocServer32 del componente come indicato di seguito:
ThreadingModel
value non presente: supporta il modello a thread singolo.ThreadingModel=Apartment
: supporta il modello STA.ThreadingModel=Both
: supporta il modello STA e MTA.ThreadingModel=Free
: supporta solo MTA.
Note
ThreadingModel
è un valore denominato, non una sottochiave di InprocServer32 come documentato erroneamente in alcune versioni precedenti della documentazione di Win32.
I modelli di threading dei server in-proc vengono illustrati più avanti in questo articolo. Se un server in-proc fornisce molti tipi di oggetti (ognuno con un 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 supporta più thread (ovvero un ThreadingModel
valore di Apartment, Both o Free).
Come indicato in precedenza, i server out-of-process non si contrassegnano usando il valore ThreadingModel. Invece, usano CoInitialize
o CoInitializeEx
. I server basati su DLL che prevedono l'esecuzione out-of-process usando la funzionalità "surrogato" di COM (ad esempio il surrogato fornito dal sistema DLLHOST.EXE) seguono le regole per i server basati su DLL; in tal caso non ci sono considerazioni particolari.
Quando il client e l'oggetto Usare modelli di threading diversi
L'interazione tra un client e un oggetto out-of-process è semplice 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 simultaneamente da più client STA o MTA, COM sincronizza le chiamate inserendo i messaggi di finestra corrispondenti nella coda dei messaggi del server. L'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-process, COM deve interpose se stesso tra il client e l'oggetto in alcuni casi. Ad esempio, un oggetto in-proc progettato per supportare il modello STA può essere chiamato simultaneamente 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 l'accesso simultaneo. COM deve invece assicurarsi che le chiamate siano sincronizzate e effettuate solo dal thread associato all'oggetto STA che "contiene". Nonostante la complessità aggiunta, tutte le combinazioni di interoperabilità del modello di threading sono consentite tra i client e gli oggetti in-proc.
Modelli di threading in server out-of-process (basati su EXE)
Di seguito sono riportate tre categorie di server out-of-process, ognuno dei quali può essere usato da qualsiasi client COM indipendentemente dal modello di threading usato da tale client:
Server modello STA:
Il server esegue il funzionamento di COM in uno o più stA. 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'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 qualsiasi variabile globale usata da più stA. Il server deve usare
CoMarshalInterThreadInterfaceInStream
eCoGetInterfaceAndReleaseStream
per effettuare il marshalling dei puntatori di interfaccia, possibilmente da altri server, tra gli stA. Facoltativamente, il server può implementareIMessageFilter
in ogni sta per controllare gli aspetti del recapito delle chiamate da COM. Un caso degenerato è il server modello a thread singolo che esegue il funzionamento COM in un'istanza sta.Server modello MTA:
Il server esegue il funzionamento di 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 del 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.
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:
Client del modello STA:
Il client esegue il funzionamento di COM nei thread associati a uno o più stA. Il client deve usare
CoMarshalInterThreadInterfaceInStream
eCoGetInterfaceAndReleaseStream
per effettuare il marshalling dei puntatori di interfaccia tra gli account del servizio token di sicurezza. Un caso degenerato è il client del modello a thread singolo che esegue il funzionamento COM in un'istanza sta. Il thread del client entra in un ciclo di messaggi fornito da COM quando effettua una chiamata in uscita. Il client può usareIMessageFilter
per gestire i callback e l'elaborazione dei messaggi di finestra durante l'attesa di chiamate in uscita e altri problemi di concorrenza.Client del modello MTA:
Il client esegue il funzionamento di COM in uno o più thread, tutti appartenenti all'MTA. Il client non deve effettuare il marshalling dei puntatori di interfaccia tra i 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 riprende quando la chiamata viene restituita. Le chiamate in arrivo arrivano su thread gestiti e creati da COM.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, ognuno dei quali può essere usato 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é in genere richiede che COM marshalling l'interfaccia tra appartamenti client. Le quattro categorie sono:
In-proc Server che supporta il threading singolo ("main" STA)- nessun
ThreadingModel
valore:Un oggetto fornito da questo server prevede l'accesso allo stesso sta client tramite il quale è stato creato. Inoltre, il server prevede che tutti i relativi punti di ingresso, ad esempio
DllGetClassObject
eDllCanUnloadNow
, e i dati globali siano accessibili dallo stesso thread (quello associato all'sta principale). I server esistenti prima dell'introduzione del multithreading in COM si trovano in questa categoria. Questi server non sono progettati per l'accesso da più thread, pertanto COM crea tutti gli oggetti forniti dal server nella sta principale del processo e le chiamate agli oggetti vengono recapitati 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 nella 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'istanza sta in cui è stato creato l'oggetto. Il marshalling tra thread è lento rispetto alla chiamata diretta, pertanto è consigliabile riscrivere questi server per supportare più stA (categoria 2).Server in-proc che supporta il modello apartment a thread singolo (più stA) contrassegnato con
ThreadingModel=Apartment
:Un oggetto fornito da questo server prevede l'accesso allo stesso sta client tramite il quale è 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ù stA del processo, pertanto il server deve progettare i relativi punti di ingresso, ad esempio
DllGetClassObject
eDllCanUnloadNow
, e i dati globali per l'uso a thread multipli. Ad esempio, se due stA di un processo creano due istanze dell'oggetto in-proc contemporaneamente,DllGetClassObject
possono essere chiamate simultaneamente da entrambi gli stA. Analogamente,DllCanUnloadNow
è necessario scrivere in modo che il server sia protetto dall'essere scaricato 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 multithread perché è accessibile da più stA client. Se il server crea una nuova istanza della class factory ogni volta che
DllGetClassObject
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'oggetto viene sempre eseguito tramite tale thread e tutte le chiamate all'oggetto vengono sincronizzate da COM. Gli appartamenti client diversi da STA 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 suoi appartamenti.
Qualsiasi client che crea un oggetto STA tramite la relativa class factory ottiene un puntatore diretto all'oggetto . Questo comportamento è diverso dagli oggetti in-proc a thread singolo, in cui solo l'sta principale del client ottiene un puntatore diretto all'oggetto e tutti gli altri stA che creano l'oggetto ottengono l'accesso all'oggetto tramite un proxy. Poiché il marshalling tra thread è lento rispetto alla chiamata diretta, la velocità può essere migliorata modificando un server in-proc a thread singolo per supportare più stA.
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 può avere un comportamento incompatibile con il modello STA. Ad esempio, tramite l'uso della coda di messaggi di Windows in modo che interrompa la pompa di messaggi di una sta. Inoltre, contrassegnando il modello di threading dell'oggetto come "Libero", l'implementatore dell'oggetto indica quanto segue: questo oggetto può essere chiamato da qualsiasi thread client, ma questo oggetto può anche passare puntatori di interfaccia direttamente (senza marshalling) a qualsiasi thread creato 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 di interfaccia da qualsiasi thread creato. Se il client è un sta sta, una chiamata diretta da un thread, diversa dal thread che ha creato l'oggetto sink sarà in errore (come illustrato in 2 sopra). Di conseguenza, COM garantisce sempre che i client nei thread associati a un sta 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é consente di eseguirli direttamente nei thread STA.
Server in-proc che supporta il modello apartment e il threading libero, contrassegnati con
ThreadingModel=Both
:Un oggetto fornito da questo server implementa la propria sincronizzazione ed è accessibile simultaneamente da più appartamenti client. Inoltre, questo oggetto viene creato e usato direttamente, anziché tramite un proxy, negli stA o nell'MTA di un processo client. Poiché questo oggetto viene usato direttamente negli stA, il server deve effettuare il marshalling di interfacce di oggetti, possibilmente da altri server, tra thread, in modo che sia garantito l'accesso a qualsiasi oggetto in modo appropriato per il threading. Inoltre, contrassegnando il modello di threading dell'oggetto come "Both", l'implementatore dell'oggetto indica quanto segue: questo oggetto può essere chiamato da qualsiasi thread client, ma qualsiasi callback da questo oggetto al client verrà eseguito solo sull'apartment in cui l'oggetto ha ricevuto il puntatore dell'interfaccia all'oggetto callback. COM consente di creare tale oggetto direttamente in un'istanza di sta e in un MTA del processo client.
Poiché qualsiasi apartment che crea tale oggetto ottiene sempre un puntatore diretto anziché un puntatore proxy,
ThreadingModel "Both"
gli oggetti forniscono miglioramenti delle prestazioni sugliThreadingModel "Free"
oggetti quando vengono caricati in una sta sta.Poiché un
ThreadingModel "Both"
oggetto è progettato anche per l'accesso MTA (è thread-safe internamente), può velocizzare le prestazioni aggregando con il gestore di marshalling fornito daCoCreateFreeThreadedMarshaler
. Questo oggetto fornito dal sistema viene aggregato in tutti gli oggetti chiamanti e i puntatori personalizzati indirizzano i puntatori all'oggetto in tutti gli appartamenti del processo. I client in qualsiasi appartamento, che si tratti di STA o MTA, possono quindi accedere direttamente all'oggetto anziché tramite un proxy. Ad esempio, un client di modello STA crea l'oggetto in-proc in STA1 e effettua il marshalling dell'oggetto a STA2. Se l'oggetto non viene aggregato 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 STA2 con un puntatore diretto all'oggettoNote
Prestare attenzione quando si aggrega con il gestore di marshalling a thread libero. Si supponga, ad esempio, che un oggetto contrassegnato come
ThreadingModel "Both"
(e anche l'aggregazione con il gestore di marshalling a thread libero) disponga di un membro dati che è un puntatore di interfaccia a un altro oggetto il cuiThreadingModel
oggetto è "Apartment". Si supponga quindi che un 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 ora contiene un puntatore diretto al secondo oggetto. Si supponga ora che STA effettua il marshalling del puntatore dell'interfaccia al primo oggetto a un altro apartment. Poiché il primo oggetto si aggrega con il gestore di marshalling a thread libero, viene assegnato un puntatore diretto al primo oggetto al secondo apartment. Se il secondo apartment chiama quindi tramite questo puntatore e se questa chiamata fa sì che il primo oggetto chiami attraverso il puntatore dell'interfaccia al secondo oggetto, si è verificato un errore, perché il secondo oggetto non deve essere chiamato direttamente dal secondo apartment. Se il primo oggetto contiene un puntatore a un proxy al secondo oggetto anziché un puntatore diretto, verrà generato un errore diverso. I proxy di sistema sono anche oggetti COM associati a uno e a un solo appartamento. Tengono traccia del loro appartamento per evitare certe circolarità. Di conseguenza, un oggetto che chiama su un proxy associato a un apartment diverso dal thread su 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à del modello di threading sono consentite tra i client e gli oggetti in-process.
COM consente a tutti i client del modello STA di operare tra loro con oggetti in-proc a thread singolo creando e accedendo all'oggetto nella sta principale del client e effettuando 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 attiva una STA "host" nel client. Questa sta host crea l'oggetto e il puntatore all'interfaccia viene sottoposto a marshalling all'MTA. Analogamente, quando un sta crea un server MTA in-proc, COM avvia un MTA host in cui viene creato e sottoposto a marshalling all'sta sta. L'interoperabilità tra il modello a thread singolo e il modello MTA viene gestita in modo analogo perché il modello a thread singolo è semplicemente 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 di effettuare il marshalling dell'interfaccia tra appartamenti client. Per altre informazioni, vedere la sezione REFERENCES riportata di seguito.
Relazione tra il modello di threading e l'oggetto Class Factory restituito
Una definizione precisa dei server in-proc caricati in appartamenti è illustrata nei due passaggi seguenti:
Se la DLL che contiene la classe server in-proc non è stata caricata in precedenza (mappata nello spazio 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.COM usa il thread (o, nel caso dell'MTA, uno dei thread) associati all'apartment "loading" per chiamare la
DllGetClassObject
funzione esportata dalla DLL che richiede il CLSID della classe necessaria. L'oggetto factory restituito viene quindi usato per creare istanze di oggetti della classe .Il secondo passaggio (la chiamata di
DllGetClassObject
da COM) viene eseguito ogni e ogni volta che un client chiamaCoGetClassObject
/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 della classe e, infine, l'autore del pacchetto DLL di un determinato set di classi ha la massima libertà di decidere quale oggetto factory restituire in risposta alla chiamata di DllGetClassObject
funzione. L'autore del pacchetto DLL ha il risultato finale, perché il codice "dietro" il singolo punto di ingresso a livello DllGetClassObject()
di DLL è ciò che ha il primo e potenzialmente il diritto finale per decidere cosa fare. Le tre possibilità tipiche sono:
DllGetClassObject
restituisce un oggetto class factory univoco per ogni thread chiamante, ovvero un oggetto class factory per STA e potenzialmente più factory di classi all'interno dell'MTA.DllGetClassObject
restituisce sempre lo stesso oggetto class factory, indipendentemente dall'identità del thread chiamante o dal tipo di apartment associato al thread chiamante.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 class factory restituito (ad esempio un nuovo oggetto class factory per ogni chiamata a DllGetClassObject
), ma non sembrano attualmente essere utili.
Riepilogo dei modelli client e di threading a 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 all'sta "main" (primo thread da chiamare o
CoInitializeEx
conCOINIT_APARTMENTTHREADED
flag)-chiamareCoInitialize
questo STA0 (detto anche modello a threading singolo). - il client è in esecuzione in un thread associato a in qualsiasi altro STA [ASCII 150] chiamare sta*.
- il client è in esecuzione in un thread associato a nell'MTA.
Tipi di server DLL:
- Il server non ha una
ThreadingModel
chiave: chiama questo "Nessuno". - Il server è contrassegnato come "Apartment"--call this "Apt".
- Il server è contrassegnato come "Gratuito".
- Il server è contrassegnato come "Entrambi".
Quando si legge la tabella seguente, tenere presente che la definizione fatta sopra di "caricamento" di un server in un appartamento.
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
.