Appartements Single-Threaded

L’utilisation d’appartements à thread unique (processus de modèle d’appartement) offre un paradigme basé sur les messages pour traiter plusieurs objets s’exécutant simultanément. Il vous permet d’écrire du code plus efficace en autorisant un thread, pendant qu’il attend la fin d’une opération chronophage, à autoriser l’exécution d’un autre thread.

Chaque thread d’un processus initialisé en tant que processus de modèle d’appartement et qui récupère et distribue des messages de fenêtre est un thread d’appartement à thread unique. Chaque fil vit dans son propre appartement. Au sein d’un appartement, les pointeurs d’interface peuvent être passés sans marshaler. Par conséquent, tous les objets d’un thread d’appartement à thread unique communiquent directement.

Un regroupement logique d’objets associés qui s’exécutent tous sur le même thread, et qui doivent donc avoir une exécution synchrone, peut vivre sur le même thread d’appartement à thread unique. Toutefois, un objet de modèle d’appartement ne peut pas résider sur plusieurs threads. Les appels à des objets dans d’autres threads doivent être effectués dans le contexte du thread propriétaire, de sorte que COM distribué change automatiquement les threads pour vous lorsque vous appelez sur un proxy.

Les modèles interprocess et interthread sont similaires. Lorsqu’il est nécessaire de passer un pointeur d’interface à un objet dans un autre appartement (sur un autre thread) au sein du même processus, vous utilisez le même modèle de marshaling que celui utilisé par les objets dans différents processus pour passer des pointeurs au-delà des limites du processus. En obtenant un pointeur vers l’objet de marshaling standard, vous pouvez marshaler des pointeurs d’interface au-delà des limites de thread (entre les appartements) de la même façon qu’entre les processus. (Les pointeurs d’interface doivent être marshalés lorsqu’ils sont passés entre les appartements.)

Les règles pour les appartements à thread unique sont simples, mais il est important de les suivre attentivement :

  • Chaque objet ne doit vivre que sur un seul thread (au sein d’un appartement à thread unique).
  • Initialisez la bibliothèque COM pour chaque thread.
  • Marshalez tous les pointeurs vers des objets lors de leur passage entre les appartements.
  • Chaque appartement monothread doit avoir une boucle de message pour gérer les appels d’autres processus et appartements au sein du même processus. Les appartements à thread unique sans objets (client uniquement) ont également besoin d’une boucle de message pour distribuer les messages de diffusion utilisés par certaines applications.
  • Les objets basés sur dll ou en cours d’exécution n’appellent pas les fonctions d’initialisation COM ; au lieu de cela, ils inscrivent leur modèle de threading avec la valeur nommée ThreadingModel sous la clé InprocServer32 dans le Registre. Les objets prenant en charge l’appartement doivent également écrire soigneusement les points d’entrée DLL. Il existe des considérations spéciales qui s’appliquent au threading des serveurs in-process. Pour plus d’informations, consultez Problèmes de thread de serveur in-process.

Alors que plusieurs objets peuvent vivre sur un seul thread, aucun objet de modèle d’appartement ne peut vivre sur plusieurs threads.

Chaque thread d’un processus client ou d’un serveur hors processus doit appeler CoInitialize, ou appeler CoInitializeEx et spécifier COINIT_APARTMENTTHREADED pour le paramètre dwCoInit . Le main appartement est le thread qui appelle CoInitializeEx en premier. Pour plus d’informations sur les serveurs in-process, consultez Problèmes de thread de serveur in-process.

Tous les appels à un objet doivent être effectués sur son thread (dans son appartement). Il est interdit d’appeler un objet directement à partir d’un autre thread ; L’utilisation d’objets de cette manière avec thread libre peut entraîner des problèmes pour les applications. L’implication de cette règle est que tous les pointeurs vers des objets doivent être marshalés lorsqu’ils sont passés entre les appartements. COM fournit les deux fonctions suivantes à cet effet :

Ces fonctions encapsulent les appels aux fonctions CoMarshalInterface et CoUnmarshalInterface , qui nécessitent l’utilisation de l’indicateur MSHCTX_INPROC.

En général, le marshaling est effectué automatiquement par COM. Par exemple, lors du passage d’un pointeur d’interface en tant que paramètre dans un appel de méthode sur un proxy à un objet d’un autre appartement, ou lors de l’appel de CoCreateInstance, COM effectue le marshaling automatiquement. Toutefois, dans certains cas particuliers, lorsque l’enregistreur d’application transmet des pointeurs d’interface entre des appartements sans utiliser les mécanismes COM normaux, l’enregistreur doit gérer le marshaling manuellement.

Si un appartement (Appartement 1) dans un processus a un pointeur d’interface et qu’un autre appartement (Appartement 2) nécessite son utilisation, l’Appartement 1 doit appeler CoMarshalInterThreadInterfaceInStream pour marshaler l’interface. Le flux créé par cette fonction est thread-safe et doit être stocké dans une variable accessible par Apartment 2. Apartment 2 doit passer ce flux à CoGetInterfaceAndReleaseStream pour démarshaler l’interface et récupérera un pointeur vers un proxy via lequel il peut accéder à l’interface. L’appartement main doit rester actif jusqu’à ce que le client ait terminé tout le travail COM (car certains objets in-process sont chargés dans l’appartement main, comme décrit dans Problèmes de thread de serveur in-process). Une fois qu’un objet a été passé entre des threads de cette manière, il est très facile de passer des pointeurs d’interface en tant que paramètres. De cette façon, le COM distribué effectue le marshaling et le basculement de thread pour l’application.

Pour gérer les appels d’autres processus et appartements au sein du même processus, chaque appartement monothread doit avoir une boucle de message. Cela signifie que la fonction de travail du thread doit avoir une boucle GetMessage/DispatchMessage. Si d’autres primitives de synchronisation sont utilisées pour communiquer entre les threads, la fonction MsgWaitForMultipleObjects peut être utilisée pour attendre à la fois les messages et les événements de synchronisation de threads. La documentation de cette fonction contient un exemple de ce type de boucle de combinaison.

COM crée une fenêtre masquée à l’aide de la classe Windows « OleMainThreadWndClass » dans chaque appartement à thread unique. Un appel à un objet est reçu en tant que message de fenêtre pour cette fenêtre masquée. Lorsque l’appartement de l’objet récupère et distribue le message, la fenêtre masquée le reçoit. La procédure de fenêtre appelle ensuite la méthode d’interface correspondante de l’objet.

Lorsque plusieurs clients appellent un objet, les appels sont mis en file d’attente dans la file d’attente des messages et l’objet reçoit un appel chaque fois que son appartement récupère et distribue des messages. Étant donné que les appels sont synchronisés par COM et que les appels sont toujours remis par le thread qui appartient à l’appartement de l’objet, les implémentations d’interface de l’objet n’ont pas besoin de fournir la synchronisation. Les appartements à thread unique peuvent implémenter IMessageFilter pour leur permettre d’annuler des appels ou de recevoir des messages de fenêtre si nécessaire.

L’objet peut être réinscrit si l’une de ses implémentations de méthode d’interface récupère et distribue des messages ou effectue un appel ORPC à un autre thread, ce qui entraîne la remise d’un autre appel à l’objet (par le même appartement). OLE n’empêche pas la réentrance sur le même thread, mais il peut aider à assurer la sécurité du thread. Cela est identique à la façon dont une procédure de fenêtre peut être réinscrite si elle récupère et distribue des messages lors du traitement d’un message. Toutefois, l’appel d’un serveur d’appartement à thread unique hors processus qui appelle un autre serveur d’appartement à thread unique permet de réinscrire le premier serveur.

Accès aux interfaces entre les appartements

Choix du modèle threading

Appartements multithreads

Problèmes de thread de serveur in-process

Processus, threads et appartements

Communication monothread et multithread