INFO : Description et fonctionnement des modèles de threads OLE


Résumé


Les objets COM peuvent être utilisés dans plusieurs threads d'un processus. Les termes « apartment (cloisonné) unique-thématique » (STA) et « Multithread apartment (cloisonné) » (MTA) sont utilisé pour créer un cadre conceptuel pour décrire la relation entre les objets et les threads, les relations de concurrence entre les objets, les moyens par quelle méthode les appels sont remis à un objet et les règles pour le passage des pointeurs d’interface entre les threads. Composants et à leurs clients le choix entre apartment (cloisonné deux) modèles suivants actuellement pris en charge par COM :

  1. Modèle d’ordonnancement cloisonné monothread (STA) : un ou plusieurs threads dans un processus utilisent COM et les appels à des objets COM sont synchronisées avec les Interfaces COM sont marshalés entre les threads. Un cas dégénéré du modèle de thread cloisonné, où un seul thread dans un processus donné utilise COM, est appelé le modèle de threads unique. Dans le passé, les informations et la documentation de Microsoft ont parfois appelé le modèle STA simplement le « modèle d'apartment (cloisonné) ».
  2. Modèle de cloisonnement multithread (MTA) : un ou plusieurs threads utilisent COM et les appels aux objets COM associés à l’agent MTA sont effectués directement par tous les threads associés à l’agent MTA sans aucun interposition du code du système entre l’appelant et l’objet. Dans la mesure où plusieurs clients simultanés peuvent appeler les objets plus ou moins simultanément (simultanément sur les systèmes multiprocesseurs), les objets doivent synchroniser leur état interne par eux-mêmes. Les interfaces ne sont pas marshalés entre les threads. Microsoft des informations et la documentation précédente ont parfois mentionné ce modèle en tant que « modèle libre de threads. »
  3. Le modèle STA et le modèle MTA peuvent être utilisés dans le même processus. Cela est parfois appelé un processus « modèle mixte ».
Le MTA a été introduit dans NT 4.0 et est disponible dans Windows 95 avec DCOM95. Le modèle STA existe dans Windows NT 3.51 et Windows 95 et NT 4.0 et Windows 95 avec DCOM95.

Plus d'informations


Vue d’ensemble

Les modèles de thread COM fournissent le mécanisme pour les composants qui utilisent les différentes architectures de threads pour travailler ensemble. Ils fournissent également des services de synchronisation aux composants qui en ont besoin. Par exemple, un objet particulier peut être conçu pour être appelé que par un seul thread et ne peut pas synchroniser des appels simultanés à partir de clients. Si un tel objet est appelé simultanément par plusieurs threads, il se bloque ou provoque des erreurs. COM fournit les mécanismes pour gérer cette interopérabilité des architectures de thread.

Même thread prenant en charge les composants doivent souvent des services de synchronisation. Par exemple, les composants qui ont une interface utilisateur graphique (GUI), tels que les contrôles OLE/ActiveX place les incorporations actives et les documents ActiveX, nécessitent une synchronisation et sérialisation des appels COM et messages de fenêtre. COM fournit ces services de synchronisation pour ces composants peuvent être écrites sans code complexe de synchronisation.

Un « apartment » a plusieurs aspects interdépendants. Tout d’abord, il s’agit d’une construction logique de réflexion à propos de l’accès concurrentiel, par exemple comment les threads sont liés à un ensemble d’objets COM. Ensuite, il est qu'un ensemble de programmeurs de règles doit respecter pour recevoir le comportement d’accès concurrentiel qu’ils s’attendent à partir de l’environnement COM. Enfin, il est le code fourni par le système qui permet aux programmeurs de gérer l’accès concurrentiel du thread en ce qui concerne les objets COM.

Le terme « apartment » provient d’une métaphore dans laquelle un processus est conçu comme une entité totalement distinctes, comme une « construction » qui est divisé en un ensemble de connexes mais différents « paramètres régionaux » appelé « apartments ». Un apartment (cloisonné) est un conteneur logique « » qui crée une association entre les objets et, dans certains cas, des threads. Les threads ne sont pas des apartments (cloisonnés), bien qu’il peut y avoir un seul thread logique associé à un « apartment » dans le modèle STA. Les objets ne sont pas des apartments (cloisonnés), bien que chaque objet est associé à « apartment » qu’une seule. Mais les appartements sont plus qu’une construction logique ; leurs règles décrivent le comportement du système COM. Si les règles des modèles apartment (cloisonné) ne sont pas suivies, les objets COM ne fonctionnera pas correctement.

Plus de détails

Un Single-threaded apartment (STA) est un ensemble d’objets COM associé à un thread particulier. Ces objets sont associés à la cloison par le thread en cours de création ou, plus précisément, tout d’abord exposée au système COM (généralement par marshaling) sur le thread. UN STA est considéré comme un endroit où un objet ou un proxy de « vie ». » Si l’objet ou un proxy doit être accessible par un autre cloisonnement (dans le même ou un autre processus), doit être marshalée à son pointeur d’interface pour cette apartment (cloisonné) où un nouveau proxy est créé. Les règles du modèle apartment (cloisonné) sont ne suivies, aucun des appels directs à partir d’autres threads du même processus sont autorisés pour cet objet ; que cela violerait la règle sur tous les objets dans un appartement donnée exécutée sur un thread unique. La règle existe, car la plupart du code s’exécutant dans un STA ne fonctionnera pas correctement si vous exécutez sur des threads supplémentaires.

Le thread associé à un STA doit appeler CoInitialize ou CoInitializeEx (COINIT_APARTMENTTHREADED, NULL) et doit récupérer et distribuer les messages de fenêtre pour les objets associés à recevoir des appels entrants. COM distribue et synchronise les appels à des objets dans un STA à l’aide de messages de fenêtre, comme décrit plus loin dans cet article.

Le « STA principal » est le thread qui appelle CoInitialize ou CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) tout d’abord au sein d’un processus donné. Le STA principal d’un processus doit rester actif jusqu'à ce que tout le travail COM est terminé, car certains objets in-process sont toujours chargés dans le STA principal, comme décrit plus loin dans cet article.

Windows NT 4.0 et DCOM95 introduisent un nouveau type d’apartment (cloisonné), appelé la cloison multithread (MTA). Un agent MTA est un ensemble d’objets COM associé à un ensemble de threads dans le processus de sorte que n’importe quel thread peut appeler toute implémentation de l’objet directement, sans interposition du code du système. Des pointeurs d’interface à n’importe quel objet dans le MTA peuvent être passées entre les threads associés à l’agent MTA sans avoir à être marshalés. Tous les threads dans le processus qui appellent CoInitializeEx (NULL, COINIT_MULTITHREADED) sont associés à l’agent MTA. Contrairement au STA décrit ci-dessus, les threads dans un MTA n’avez pas besoin de récupérer et de distribuer les messages de fenêtre pour les objets associés à recevoir des appels entrants. COM ne synchronise pas les appels à des objets dans un MTA. Objets dans un MTA doivent protéger leur état interne à partir de la corruption par l’interaction de plusieurs threads simultanés, et ils ne peuvent pas faire de suppositions sur le contenu de constante restant de stockage Local des threads entre les différents appels de méthodes.

Un processus peut avoir n’importe quel nombre de stations, mais, au plus, peut y avoir un seul MTA. Le MTA est constitué d’un ou plusieurs threads. Stations ont un seul thread. Un thread appartient, au plus, un apartment (cloisonné). Les objets appartiennent à apartment (cloisonné) qu’une seule. Des pointeurs d’interface doivent toujours être marshalées entre les apartments (bien que le résultat du marshaling peut être un pointeur direct plutôt qu’un serveur proxy). Consultez les informations ci-dessous sur CoCreateFreeThreadedMarshaler.

Un processus choisit l’un des modèles de thread fournies par COM. Un processus de modèle STA a un ou plusieurs des stations et ne dispose pas d’un agent MTA. Un processus de modèle MTA a un MTA avec un ou plusieurs threads et ne dispose pas des stations. Un modèle mixte de processus a un MTA et n’importe quel nombre de stations.

Modèle de thread cloisonné

Le thread d’un STA doit appeler CoInitialize ou CoInitializeEx (COINIT_APARTMENTTHREADED, NULL) et doit récupérer et distribuer des messages de fenêtre, car COM utilise des messages de fenêtre pour synchroniser et d’expédier la livraison des appels à un objet dans ce modèle. Consultez la section références ci-dessous pour plus d’informations.

Serveur qui prend en charge le modèle STA :

Dans le modèle STA, les appels à un objet sont synchronisées par COM de la même manière que les messages de fenêtre validées dans une fenêtre. Les appels sont remis à l’aide de messages de fenêtre pour le thread qui a créé l’objet. Par conséquent, le thread de l’objet doit appeler Get/PeekMessage et à DispatchMessage pour recevoir des appels. COM crée une fenêtre masquée associée à chaque STA. Un appel à un objet extérieur le STA est transféré par le runtime COM. au thread de l’objet à l’aide d’un message de fenêtre validé dans cette fenêtre cachée. Lorsque le thread associé à STA l’objet récupère et distribue le message, la procédure de fenêtre pour la fenêtre masquée, également mis en œuvre par COM, le reçoit. La procédure de fenêtre est utilisée par le runtime COM. à « raccorder » le thread associé le STA, car le runtime COM est sur les deux côtés de l’appel à partir du thread propriétaire de COM à thread du STA. Le runtime COM (en cours d’exécution dans le thread de le STA) s’appelle « up » via un stub fournis par COM dans la méthode d’interface correspondante de l’objet. Le chemin d’accès de l’exécution du retour de l’appel de la méthode annule l’appel « haut » ; l’appel retourne le stub et le runtime COM, qui repasse le contrôle à la thread d’exécution COM via un message de la fenêtre, puis renvoie via le canal de COM à l’appelant d’origine.

Lorsque plusieurs clients appellent un objet STA, les appels sont automatiquement mis en attente dans la file d’attente par le transfert de mécanisme de contrôle utilisé dans le STA. L’objet reçoit un appel chaque fois que son STA récupère et distribue les messages. Dans la mesure où les appels sont synchronisés par COM de cette manière, et les appels sont toujours transmis sur le thread unique associé à STA l’objet, des implémentations d’interface de l’objet n’est pas nécessaire assurer la synchronisation.

Remarque: l’objet peut être entrée à nouveau si une implémentation de méthode d’interface récupère et distribue les messages pendant le traitement d’un appel de méthode à l’origine d’un autre appel à être remis à l’objet en le STA de même. Est d’une manière courante dans laquelle ce problème se produit si un objet STA effectue un appel à (cross-apartment (cloisonné) / interprocessus) sortant à l’aide de COM. Cette page est identique à la façon dans laquelle une procédure de fenêtre peut être entrée à nouveau s’il extrait et distribue les messages pendant le traitement d’un message. COM n’empêche pas de re-entrée sur la même thread, mais n’empêche pas l’exécution en simultané. Il fournit également un moyen par lequel COM. de réentrance peut être géré. Consultez la section références ci-dessous pour plus d’informations. L’objet n’est pas ré-si les implémentations de méthode ne pas appel de son appartement ou sinon, récupérer et distribuer les messages.

Responsabilités du client dans le modèle STA :

Code de client qui s’exécute dans un processus ou un thread qui utilise le modèle STA doit marshaler des interfaces d’un objet entre les apartments (cloisonnés) à l’aide de CoMarshalInterThreadInterfaceInStream et CoGetInterfaceAndReleaseStream. Par exemple, si 1 de cloisonnement dans le client dispose d’un pointeur d’interface et Appartement 2 requiert l’utilisation de ce dernier, Appartement 1 doit marshaler l’interface à l’aide de CoMarshalInterThreadInterfaceInStream. L’objet de flux retourné par cette fonction est thread-safe et son pointeur d’interface qui doit être stocké dans une variable direct à la mémoire accessible par Appartement 2. Appartement 2 doit transmettre cette interface de flux à CoGetInterfaceAndReleaseStream pour désorganiser l’interface sur l’objet sous-jacent et obtenir un pointeur vers un proxy par le biais duquel il peut accéder à l’objet.

Le compartiment principal d’un processus donné doit rester actif jusqu'à ce que le client a terminé toutes les tâches de COM, car certains objets in-process sont chargées dans l’apartment de main. (Informations plus détaillées ci-dessous).

Modèle cloisonné multi-thread

Un agent MTA est la collection d’objets créés ou exposés par tous les threads dans le processus qui ont appelé CoInitializeEx (NULL, COINIT_MULTITHREADED).

Remarque: les implémentations actuelles de COM permettent à un thread qui n’initialise pas explicitement de COM pour faire partie de l’agent MTA. Un thread qui n’initialise pas COM est la partie du MTA uniquement si elle commence à l’aide de COM après qu’au moins un autre thread dans le processus a été appelée au préalable CoInitializeEx (NULL, COINIT_MULTITHREADED). (Il est même possible que le MTA peut initialisé COM lui-même lorsque aucun thread client n’a fait explicitement ; par exemple, un thread associé à un STA appelle CoGetClassObject/CoCreateInstance [Ex] sur un CLSID qui est marqué comme « ThreadingModel = libre » et COM crée implicitement un MTA dans lequel l’objet de classe est chargée.) Consultez les informations sur les threads de l’interopérabilité du modèle ci-dessous.

Toutefois, il s’agit d’une configuration qui peut provoquer des problèmes, tels que les violations d’accès, dans certaines circonstances. Par conséquent, il est fortement recommandé que chaque thread que doit faire le travail de COM initialiser COM en appelant CoInitializeEx et ensuite, à la fin du travail de COM, appeler CoUninitialize. Le coût de « inutilement » l’initialisation du MTA est minime.

Threads MTA n’avez pas besoin de récupérer et de distribuer les messages car COM n’utilise pas les messages de fenêtre dans ce modèle pour fournir des appels à un objet.

Serveur qui prend en charge le modèle MTA :

Dans le modèle MTA, les appels à un objet ne sont pas synchronisés par COM. les multiples clients peuvent appeler simultanément d’un objet que prend en charge ce modèle sur différents threads et que l’objet doit fournir la synchronisation dans ses implémentations de la méthode d’interface à l’aide de synchronisation des objets tels que les événements, les mutex, sémaphores, etc.. Objets MTA peuvent recevoir des appels simultanés à partir de plusieurs clients out-of-process dans un pool de threads créés par COM appartenant au processus de l’objet. Objets MTA peuvent recevoir des appels simultanés à partir de plusieurs clients dans le processus sur plusieurs threads associés à l’agent MTA.

Responsabilités du client dans le modèle MTA :

Code de client qui s’exécute dans un processus ou un thread qui utilise le modèle MTA ne dispose pas de marshaler des pointeurs d’interface d’un objet entre lui-même et les autres threads MTA. Au lieu de cela, un thread MTA peut utiliser un pointeur d’interface obtenu à partir d’un autre thread MTA comme un pointeur direct à la mémoire. Lorsqu’un thread client effectue un appel à un objet out-of-process, il interrompt jusqu'à ce que l’appel est terminé. Les appels peuvent arriver sur les objets associés avec le MTA, tandis que tous les threads créés par l’application associées à l’agent MTA sont bloqués sur les appels de sortie en cours. Dans ce cas et en règle générale, les appels entrants sont remis sur les threads fournis par le runtime COM.. Filtres de messages (IMessageFilter) ne sont pas disponibles pour une utilisation dans le modèle MTA.

Modèle mixte de threads

Un processus qui prend en charge le modèle mixte-threading utilisera un MTA et une ou plusieurs des stations. Des pointeurs d’interface doivent être marshalés entre les apartments (cloisonnés), mais peuvent être utilisées sans marshaling dans le MTA. Les appels à des objets dans un STA sont synchronisés par COM pour s’exécuter uniquement sur un thread alors que les appels aux objets dans le MTA ne sont pas. Toutefois, les appels dans un STA à MTA passent par le code fourni par le système normalement et basculer à partir du thread STA sur un thread MTA avant d’être remis à l’objet.

Remarque: consultez la documentation du Kit de développement logiciel de CoCreateFreeThreadedMarshaler() et de la discussion de cette API ci-dessous pour plus d’informations sur les cas où les pointeurs directs peuvent être utilisés, et comment un thread STA peut appeler directement dans un objet première association avec le MTA et, Inversement, à partir de plusieurs appartements.

Choix du modèle de thread

Un composant peut choisir prendre en charge le modèle STA, le modèle MTA ou une combinaison des deux en utilisant le modèle mixte de threads. Par exemple, un objet qui effectue des e/s étendues peut choisir prendre en charge des MTA pour fournir une réponse maximale aux clients en autorisant les appels d’interface à effectuer au cours de la latence d’e/s. Sinon, un objet qui interagit avec l’utilisateur, presque toujours choisit prendre en charge le STA pour synchroniser les appels COM avec ses opérations d’interface utilisateur. Il est plus facile de prise en charge le modèle STA, car COM fournit la synchronisation. Le modèle MTA de prise en charge est plus difficile, car l’objet doit implémenter la synchronisation, mais la réponse aux clients est préférable parce que la synchronisation est utilisée pour les sections plus petites du code, plutôt que pour l’appel de l’ensemble de l’interface fournie par COM.

Le modèle STA est également utilisé par Microsoft Transaction Server (MTS, précédemment nom de code « Viper »), et donc les objets de base de DLL de planification à exécuter dans l’environnement MTS doivent utiliser le modèle STA. Objets mis en œuvre pour le modèle MTA normalement fonctionne correctement dans un environnement MTS. Toutefois, elles s’exécuteront moins efficace car il utilise les primitives de synchronisation de thread inutiles.

Le modèle de prise en charge de threads, les serveurs In-Process de marquage

Un thread utilise le modèle MTA s’il appelle CoInitializeEx (NULL, COINIT_MULTITHREADED) ou utilise COM sans l’initialiser. Un thread utilise le modèle STA si elle appelle CoInitialize ou CoInitializeEx (NULL, COINIT_APARTMENTTHREADED).

Les API de CoInitialize offrent un contrôle d’apartment (cloisonné) pour le code client et pour les objets qui sont fournies dans. Exe, car le code de démarrage du runtime COM peut initialiser COM de la manière souhaitée.

Toutefois, un serveur in-process (basée sur une DLL) de COM n’appelle pas CoInitialize/CoInitializeEx car ces API sera appelé au moment où que le serveur DLL est chargé. Par conséquent, un serveur DLL doit utiliser le Registre pour informer COM au modèle de thread qu'il prend en charge afin que COM peut s’assurer que le système fonctionne d’une manière qui soit compatible avec elle. Une valeur nommée de clé de CLSID\InprocServer32 du composant appelé ThreadingModel est utilisée à cette fin comme suit :

  • Valeur ThreadingModel n’existe pas : prend en charge le modèle de threads unique.
  • ThreadingModel = apartment (cloisonné) : prend en charge STA modèle.
  • ThreadingModel = Both : prend en charge modèle STA et MTA.
  • ThreadingModel = libre : prend en charge des MTA uniquement.
Remarque: ThreadingModel est une valeur nommée, pas une sous-clé de InprocServer32 comme mal documenté dans certaines versions antérieures de la documentation de Win32.

Les serveurs in-process des modèles de thread sont abordés plus loin dans cet article. Si un serveur in-process fournit de nombreux types d’objets (chacun avec sa propre CLSID unique), chaque type peut avoir une autre valeur ThreadingModel. En d’autres termes, le modèle de thread est par CLSID, pas par code/DLL du package. Toutefois, l’API désigne nécessaire de « amorçage » et interrogent tous les serveurs in-process (DLLGetClassObject(), DLLCanUnloadNow()) doit être thread-safe pour n’importe quel serveur in-process qui prend en charge plusieurs threads (c'est-à-dire la valeur ThreadingModel apartment (cloisonné), les deux ou libre ).

Comme indiqués précédemment, out-of-process des serveurs ne marquent pas eux-mêmes à l’aide de la valeur ThreadingModel. Au lieu de cela, ils utilisent CoInitialize ou CoInitializeEx. Serveurs basés sur les DLL qui comptent s’exécuter out-of-process à l’aide de la fonctionnalité « de substitution » de COM (par exemple, la substitution fourni par le système DLLHOST. (EXE) suivez simplement les règles pour les serveurs de base de DLL ; Il n’y a dans ce cas aucune considération particulière.

Lorsque le Client et l’objet utilisent différents modèles de thread

Interaction entre un client et un objet out-of-process est simple même lorsque les différents modèles de thread sont utilisées, car le client et l’objet sont dans des processus différents et COM est impliqué en passant des appels à partir du client de l’objet. Dans la mesure où COM est interposé entre le client et le serveur, il fournit le code pour l’interopérabilité des modèles de thread. Par exemple, si un objet STA est appelé simultanément par des clients de plusieurs STA ou MTA, COM synchronise les appels en plaçant des messages de fenêtre correspondante dans la file d’attente de messages du serveur. STA l’objet reçoit un appel chaque fois qu’il récupère et distribue les messages. Toutes les combinaisons d’interopérabilité de modèles de threads autorisés et entièrement pris en charge entre les clients et les objets d’out-of-process.

Interaction entre un client et un objet in-process qui utilise différents modèles de thread est plus complexe. Bien que le serveur soit in-process, COM doit s'interposer entre le client et l'objet dans certains cas. Par exemple, un objet en cours qui prend en charge le modèle STA peut-être être appelé simultanément par plusieurs threads d'un client. COM ne peut pas permettre au client de threads accéder directement à l’interface l’objet car l’objet n’est pas conçu pour ce type d’accès simultané. Au lieu de cela, COM doit garantir que les appels sont synchronisées et effectuées uniquement par le thread associé le STA qui « contient » l’objet. En dépit de la complexité, toutes les combinaisons d’interopérabilité du modèle de thread sont autorisées entre les clients et les objets in-process.

Modèles de Out-of-Process (EXE) serveurs de thread

Voici les trois catégories de serveurs out-of-process, qui peut être utilisé par n’importe quel client COM, quel que soit le modèle de thread utilisé par ce client :

  1. Serveur de modèle STA :

    Le serveur effectue le travail de COM dans un ou plusieurs des stations. Les appels entrants sont synchronisées par COM et livrées par le thread associé le STA dans laquelle l’objet a été créé. Les appels de méthode de fabrique de classe sont remis par le thread associé le STA qui inscrit la fabrique de classe. La fabrique de classe et de l’objet n’est pas nécessaire d’implémenter la synchronisation. Toutefois, l’implémentation doit synchroniser l’accès à toutes les variables globales utilisées par plusieurs stations. Le serveur doit utiliser CoMarshalInterThreadInterfaceInStream et CoGetInterfaceAndReleaseStream pour marshaler des pointeurs d’interface, éventuellement à partir d’autres serveurs, entre les stations. Le serveur peut éventuellement implémenter IMessageFilter dans chaque STA pour contrôler des aspects de la remise d’appel par COM. Un cas dégénéré est le serveur de modèle unique-threading qui effectue le travail de COM dans un STA
  2. Serveur de modèle MTA :

    Le serveur effectue le travail de COM dans un ou plusieurs threads qui appartiennent au MTA. Les appels ne sont pas synchronisés par COM. COM crée un pool de threads dans le processus du serveur, et un appel client est remis par un de ces threads. Threads n’avez pas besoin de récupérer et de distribuer les messages. La fabrique d’objet et de la classe doit implémenter la synchronisation. Le serveur n’a pas besoin de marshaler des pointeurs d’interface entre les threads.
  3. Serveur de modèle mixte :

    Reportez-vous à la section de cet article intitulée « mixte modèle de threads » pour plus d’informations.

Modèles dans les Clients de thread

Il existe trois catégories de clients :

  1. Client du modèle STA :

    Le client de COM fonctionne dans les threads associés à un ou plusieurs des stations. Le client doit utiliser CoMarshalInterThreadInterfaceInStream et CoGetInterfaceAndReleaseStream pour marshaler des pointeurs d’interface entre les stations. Un cas dégénéré est le client du modèle unique-threading qui effectue le travail de COM dans un STA Thread que le client entre COM fournie la boucle de message lorsqu’il effectue un appel sortant. Le client peut utiliser IMessageFilter pour gérer les rappels et le traitement des messages de la fenêtre lors de l’attente sur sortant des appels et autres problèmes de concurrence d’accès.
  2. Client du modèle MTA :

    Le client effectue COM dans un ou plusieurs threads qui appartiennent au MTA. Le client n’a pas besoin de marshaler des pointeurs d’interface entre ses threads. Le client ne peut pas utiliser IMessageFilter. Suspendre les threads du client lorsqu’il effectue un appel de COM pour un objet out-of-process et reprend lorsque l’appel retourne. Appels entrants arrivent sur les threads créés par COM et managées.
  3. Client du modèle mixte :

    Reportez-vous à la section de cet article intitulée « mixte modèle de threads » pour plus d’informations.

Modèles dans In-Process (DLL) serveurs de thread :

Il existe quatre catégories de serveurs in-process, qui peut être utilisé par n’importe quel client COM, quel que soit le modèle de thread utilisé par ce client. Cependant, les serveurs in-process doivent fournir code de marshaling pour toute interface personnalisée (non-définie par le système) qu’ils implémentent s’ils doivent prendre en charge l’interopérabilité le modèle thread car qui nécessite généralement que marshal COM l’interface entre le client apartments (cloisonnés). Les quatre catégories sont les suivantes :

  1. Serveur in-process qui prend en charge le thread STA (single « main »)-aucune valeur ThreadingModel :

    Un objet fourni par ce serveur s’attend à être accessible par le même STA de client par lequel il a été créé. En outre, le serveur s’attend à tous ses points d’entrée, tels que DllGetClassObject DllCanUnloadNow et les données globales d’être accessible par le même thread (celle associée le STA principal). Les serveurs qui existait avant l’introduction de multi-threading dans COM appartiennent à cette catégorie. Ces serveurs ne sont pas conçus pour être accessibles par plusieurs threads pour COM crée tous les objets fournis par le serveur dans le STA principal du processus et les appels aux objets sont remis par le thread associé le STA principal. Autres apartments (cloisonnés) clients accéder à l’objet par le biais de serveurs proxy. Appels à partir d’autres apartments (cloisonnés) accédez à partir du proxy au relais dans le STA principal (marshaling inter-threads), puis à l’objet. Ce regroupement permet de COM pour synchroniser les appels à l’objet et les appels sont remis par le STA dans laquelle l’objet a été créé. Marshaling inter-threads est lent par rapport à l’appelant direct, il est donc recommandé que ces serveurs soient réécrites pour prendre en charge les stations multiples (catégorie 2).
  2. Serveur in-process prenant en charge le modèle de thread unique cloisonné (STA plusieurs) - marqué avec ThreadingModel = apartment (cloisonné) :

    Un objet fourni par ce serveur s’attend à être accessible par le même STA de client par lequel il a été créé. Par conséquent, il est similaire à un objet fourni par un seul thread serveur in-process. Toutefois, les objets fournis par ce serveur peuvent être créés dans plusieurs stations du processus, afin que le serveur doit concevoir ses points d’entrée, tels que DllGetClassObject, DllCanUnloadNow et globale des données pour une utilisation multithread. Par exemple, si deux stations d’un processus de créent simultanément les deux instances de l’objet in-process, DllGetClassObject peut-être être appelé simultanément par les deux stations. De même, DllCanUnloadNow doit être écrits afin que le serveur est protégé contre les déchargé lorsque le code est en cours d’exécution sur le serveur.

    Si le serveur fournit uniquement une instance de la fabrique de classe pour créer tous les objets, la mise en oeuvre de fabrique de classe doit également être adapté pour une utilisation multithread, car il est accessible par plusieurs clients de stations. Si le serveur crée une nouvelle instance de la fabrique de classe chaque DllGetClassObject est appelée, la fabrique de classe n’a pas besoin d’être thread-safe. Toutefois, l’implémentation doit synchroniser l’accès à toutes les variables globales.

    Les objets COM créés par la fabrique de classe n’est pas nécessaire être thread-safe. Toutefois, l’accès des variables globales doivent être synchronisées par l’implémenteur. Une fois créé par un thread, l’objet est toujours accédé par le biais de thread synchronisation et tous les appels à l’objet par les apartments (cloisonnés) COM Client différents que le STA dans laquelle l’objet a été créé doit accéder à l’objet par le biais de serveurs proxy. Ces proxies sont créés lorsque le client marshale l’interface entre les apartments (cloisonnés).

    Tout client qui crée un objet STA, par le biais de la fabrique de classe obtient un pointeur direct vers l’objet. Cela est différent de celui des objets monothread in-process, où seul le STA principal du client obtient un pointeur direct à l’objet et toutes les autres stations qui créent le gain de l’accès à l’objet via un proxy. Car il est lent par rapport à l’appel direct de marshaling inter-threads, vitesse peut être améliorée considérablement en modifiant un seul thread serveur in-process pour la prise en charge de plusieurs stations.
  3. Serveur in-process prenant en charge uniquement les MTA - marqué avec ThreadingModel = libre :

    Un objet fourni par ce serveur est sécurisé pour seulement le MTA. Il implémente sa propre synchronisation et est accessible par plusieurs threads clients en même temps. Ce serveur peut présenter un comportement qui n’est pas compatible avec le modèle STA. (Par exemple, par son utilisation de la file d’attente de messages Windows d’une manière qui interrompt la pompe de messages de modèle STA.) En outre, en marquant le modèle de thread de l’objet en tant que « Libre », l’implémentation de l’objet est suivant s’affiche : cet objet peut être appelé à partir de n’importe quel thread du client, mais cet objet également peut passer des pointeurs d’interface directement (sans marshaling) pour tous les threads qui il créé et ces threads peuvent effectuer des appels à travers ces pointeurs. Par conséquent, si le client passe un pointeur d’interface vers un objet implémenté par le client (par exemple, un récepteur) à cet objet, il peut choisir de rappeler par ce pointeur d’interface à partir de n’importe quel thread qu’il a créé. Si le client est un STA, un appel direct à partir d’un thread qui est différent du thread qui a créé l’objet récepteur sera erreur (comme illustré dans les 2 ci-dessus). Par conséquent, COM toujours permet de garantir que les clients dans les threads associés à un STA accéder à ce type d’objet in-process uniquement par l’intermédiaire d’un proxy. En outre, ces objets ne doivent pas regrouper avec le marshaleur libre de threads car qui leur permet de s’exécuter directement sur des threads STA.
  4. Serveur in-process qui prend en charge le modèle cloisonné et libre de threads - marqué avec ThreadingModel = les deux :

    Un objet fourni par ce serveur implémente sa propre synchronisation et est accessible simultanément par plusieurs appartements de client. En outre, cet objet est créé et utilisé directement, au lieu de via un proxy, STA ou MTA d’un processus client. Dans la mesure où cet objet est utilisé directement dans les stations, le serveur doit marshaler des interfaces d’objets, et éventuellement d’autres serveurs, entre les threads de sorte que son accès à n’importe quel objet de manière appropriée de thread est garanti. En outre, en marquant le modèle de thread de l’objet en tant que « Les deux », l’implémentation de l’objet est suivant s’affiche : cet objet peut être appelé à partir de n’importe quel thread du client, mais tous les rappels à partir de cet objet sur le client seront effectuées uniquement sur le « apartment » dans lequel l’objet re Reç le pointeur d’interface vers l’objet de rappel. COM permet à ce type d’objet doit être créé directement dans un STA, ainsi que dans un agent MTA du processus client.

    Car n’importe quel apartment (cloisonné) qui crée un tel objet toujours obtienne un pointeur direct plutôt qu’un pointeur de proxy, ThreadingModel « Both » objets fournissent des améliorations de performances sur les objets de « Libre » ThreadingModel lors du chargement dans un STA.

    Un ThreadingModel « Les deux » objet est également conçu pour accès MTA (il est en interne complètement thread-safe), elle peut accélérer les performances par agrégation avec le marshaleur fourni par CoCreateFreeThreadedMarshaler. Cet objet fourni par le système est regroupé dans les objets en appelant et personnalisé marshale direct des pointeurs vers l’objet dans toutes les apartments (cloisonnés) dans le processus. Clients dans n’importe quel apartment, STA et MTA, peuvent alors accéder à l’objet directement au lieu de via un proxy. Par exemple, un client du modèle STA crée l’objet en cours dans STA1 et marshale l’objet STA2. Si l’objet n’agrège pas le marshaleur libre de threads avec des, STA2 accède à l’objet via un proxy. Dans ce cas, le marshaleur libre de threads fournit STA2 avec un pointeur direct vers l’objet

    Remarque: être particulièrement vigilant lors de l’agrégation avec le marshaleur libre de threads. Par exemple, supposons qu’un objet qui est marqué comme ThreadingModel « Les deux » (et en les regroupant par le marshaleur libre de threads) possède une donnée membre qui est un pointeur d’interface vers un autre objet dont ThreadingModel est « Apartment ». Puis supposent qu’un STA crée le premier objet et lors de la création, le premier objet crée le deuxième objet. Selon les règles décrites ci-dessus, le premier objet détient désormais un pointeur direct vers le deuxième objet. Supposons maintenant que le STA marshale le pointeur d’interface vers le premier objet à un autre appartement. Car les première agrégats d’objet avec la libre - threaded marshaleur, un pointeur direct vers le premier objet est donné à la cloison du deuxième. Si le cloisonnement deuxième appelle ensuite via ce pointeur et que cet appel entraîne le premier objet à appeler via le pointeur d’interface vers le deuxième objet, puis une erreur s’est produite, car le deuxième objet n’est pas censé être appelée directement à partir du deuxième apartment (cloisonné). Si le premier objet détient un pointeur à un proxy vers le deuxième objet, plutôt qu’un pointeur direct, cela provoquera une erreur différente. Proxy du système est également des objets COM qui sont associés au cloisonnement qu’un seul. Leur suivi de leur apartment (cloisonné) afin d’éviter certains circularities. Ainsi, un objet appelant sur un proxy associé à un « apartment » autre que le thread sur lequel l’objet est en cours d’exécution reçoit le retour RPC_E_WRONG_THREAD à partir du proxy et l’appel échouera.

Interopérabilité du modèle entre les Clients et les objets de processus de Threading

Toutes les combinaisons d’interopérabilité du modèle de thread sont autorisées entre les clients et les objets dans le processus.

COM permet à tous les clients du modèle STA interopérer avec des objets intra-unique-threading en accédant à l’objet dans les STA principal du client et à marshaler vers le client qui a appelé CoCreateInstance [Ex] STA.

Si un agent MTA dans un client crée un serveur in-process du modèle STA, COM tourne « hôte » STA dans le client. Cet hôte STA crée l’objet et le pointeur d’interface est marshalé vers le MTA. De même, lorsqu’un STA crée un serveur in-process MTA, COM tourne un MTA d’hôte dans lequel l’objet est créé et arrière marshalé à l’interopérabilité STA entre le modèle de threads unique et le modèle MTA est gérée de la même façon, car le modèle de threads unique est un dégénérée cas du modèle STA.

Marshaling de code doit être fournie pour n’importe quelle interface personnalisé implémentant un serveur in-process s’il souhaite prendre en charge de l’interopérabilité qui requiert COM pour le marshaling de l’interface entre les apartments (cloisonnés) clients. Consultez la section références ci-dessous pour plus d’informations.

Relation entre le modèle de thread et l’objet de fabrique de classe retourné

Une définition précise des serveurs in-process « chargées dans « appartements est expliquée dans les deux étapes suivantes :

  1. Si la DLL qui contient la classe de serveur in-process n’a pas encore été chargé (mappés dans l’espace d’adressage de processus) par le chargeur du système d’exploitation, cette opération est effectuée et COM Obtient l’adresse de la fonction DLLGetClassObject exportée par la DLL. Si la DLL a déjà été chargée par un thread associé à n’importe quel apartment (cloisonné), cette étape est ignorée.
  2. COM utilise le thread (ou, dans le cas d’un des threads MTA) associés à le « apartment » « chargement » pour appeler la fonction DllGetClassObject exportée par la DLL de demander le CLSID de la classe nécessaire. L’objet de fabrique retournée est ensuite utilisé pour créer des instances des objets de la classe.

    La deuxième étape (l’appel DllGetClassObject par COM) se produit chaque fois qu’un client appelle CoGetClassObject/CoCreateIntance [Ex], même à partir de dans le même apartment. En d’autres termes, DllGetClassObject peut être appelée de nombreuses fois par un thread associé à la même apartment (cloisonné) ; tout repose sur le nombre de clients dans cette cloison essayez d’obtenir l’accès à un objet de fabrique de classe pour cette classe.
Les auteurs des implémentations de la classe et, finalement, l’auteur du package DLL d’un ensemble de classes ont la pleine liberté de décider quel objet usine pour renvoyer en réponse à l’appel de la fonction DllGetClassObject. L’auteur du package DLL a l’ultime dites le code étant « derrière » le point d’entrée unique à l’échelle de la DLL DllGetClassObject() qu’a le droit de premier et le dernier potentiellement à décider quoi faire. Les trois causes les plus courantes sont les suivantes :

  1. DllGetClassObject retourne un objet de fabrique de classe unique par le thread appelant (ce qui signifie qu’un seul objet de fabrique de classe par STA et potentiellement plusieurs fabriques de classes dans le MTA).
  2. DllGetClassObject retourne toujours le même objet de fabrique de classe, quel que soit l’identité de la thread d’appel ou le type d’apartment (cloisonné) associée au thread appelant.
  3. DllGetClassObject retourne un objet de fabrique de classe unique par appel apartment (cloisonné) (un par Apartment (cloisonné) STA et MTA).
Il existe autres possibilités pour la relation entre les appels à DllGetClassObject et l’objet de fabrique de classe retournés (par exemple, un nouvel objet de fabrique de classe par appel à DllGetClassObject), mais elles n’apparaissent pas actuellement être particulièrement utile.

Résumé du Client et l’objet modèles de thread pour les serveurs In-Process

Le tableau suivant résume l’interaction entre les différents modèles de thread lorsqu’un client thread premier appelle CoGetClassObject sur une classe qui est implémenté comme un serveur in-process.

Types de clients/threads :

  • client s’exécute dans un thread associé le STA « main » (premier thread à appeler CoInitialize ou CoInitializeEx avec indicateur de COINIT_APARTMENTTHREADED)-appelez cette STA0 (également appelé modèle de thread unique).
  • client s’exécute dans un thread associé dans n’importe quel autre appel STA [ASCII 150] STA *.
  • client s’exécute dans un thread associé dans le MTA.
Types de serveurs DLL :

  • Serveur ne possède aucune clé ThreadingModel--nous l’appellerons « None ».
  • Serveur est marqué « Apartment »--appeler cette « Apt. »
  • Serveur est marqué « Libre ».
  • Serveur est marqué « Both ».
Lors de la lecture du tableau ci-dessous, gardez l’esprit le fait au-dessus de la définition de « chargement » d’un serveur dans un appartement.

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

Références


Documentation du Kit de développement logiciel sur l’interface IMessageFilter et de CoRegisterMessageFilter().

Pour plus d’informations, consultez les articles suivants dans la Base de connaissances Microsoft :
136885 OLE Threads doivent distribuer les Messages
137629 de l’Interface intra-objet personnalisé dans le Client du modèle d’apartment (cloisonné)