Single-Threaded Appartements

L’utilisation d’appartements à thread unique (processus de modèle d’appartement) offre un paradigme basé sur les messages pour traiter plusieurs objets en cours d’exécution simultanément. Il vous permet d’écrire du code plus efficace en autorisant un thread, alors qu’il attend une opération gourmande en temps, pour permettre à un autre thread d’être exécuté.

Chaque thread d’un processus initialisé en tant que processus de modèle d’appartement et qui récupère et répartit les messages de fenêtre, est un thread d’appartement à thread unique. Chaque fil vit dans son propre appartement. Dans un appartement, les pointeurs d’interface peuvent être transmis sans marshaling, et par conséquent, tous les objets d’un thread d’appartement à thread unique communiquent directement.

Un regroupement logique d’objets connexes qui s’exécutent sur le même thread, et doit 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 aux objets dans d’autres threads doivent être effectués dans le contexte du thread propriétaire, de sorte que les threads COM distribués basculent automatiquement lorsque vous appelez un proxy.

Les modèles interprocess et interthread sont similaires. Quand il est nécessaire de passer un pointeur d’interface à un objet d’un autre appartement (sur un autre thread) dans le même processus, vous utilisez le même modèle de marshaling que les objets dans différents processus utilisent pour passer des pointeurs entre les limites de processus. En obtenant un pointeur vers l’objet de marshaling standard, vous pouvez marshaler les pointeurs d’interface entre les limites de thread (entre appartements) de la même façon que vous effectuez 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 doit vivre sur un seul thread (dans un appartement à thread unique).
  • Initialisez la bibliothèque COM pour chaque thread.
  • Marshalez tous les pointeurs vers des objets lors de leur passage entre appartements.
  • Chaque appartement à thread unique 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 que certaines applications utilisent.
  • Les objets basés sur dll ou in-process n’appellent pas les fonctions d’initialisation COM ; Au lieu de cela, ils inscrivent leur modèle de thread avec la valeur nommée ThreadingModel sous la clé InprocServer32 dans le Registre. Les objets prenant en compte l’appartement doivent également écrire soigneusement des 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.

Bien que plusieurs objets puissent 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 . L’appartement principal est le thread qui appelle d’abord CoInitializeEx . 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 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 vers un objet d’un autre appartement ou lors de l’appel de CoCreateInstance, COM effectue automatiquement le marshaling. Toutefois, dans certains cas spéciaux, où l’enregistreur d’applications passe des pointeurs d’interface entre les appartements sans utiliser les mécanismes COM normaux, l’enregistreur doit gérer manuellement le marshaling.

Si un appartement (Appartement 1) dans un processus a un pointeur d’interface et 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. L’appartement 2 doit passer ce flux à CoGetInterfaceAndReleaseStream pour annuler l’interface et récupérer un pointeur vers un proxy via lequel il peut accéder à l’interface. L’appartement principal doit rester vivant jusqu’à ce que le client ait terminé tout le travail COM (car certains objets in-process sont chargés dans l’appartement principal, comme décrit dans les problèmes de thread de serveur in-process). Une fois qu’un objet a été passé entre les threads de cette façon, il est très facile de passer des pointeurs d’interface en tant que paramètres. De cette façon, distributed COM effectue le marshaling et le changement de thread pour l’application.

Pour gérer les appels d’autres processus et appartements au sein du même processus, chaque appartement à thread unique 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 les messages et les événements de synchronisation de threads. La documentation de cette fonction présente un exemple de cette sorte 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 la 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 de 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 recréé si l’une de ses implémentations de méthode d’interface récupère et distribue des messages ou effectue un appel ORPC vers 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 elle peut aider à assurer la sécurité des threads. 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 au premier serveur d’être entré.

Accès aux interfaces entre appartements

Choix du modèle de thread

Appartements multithreads

Problèmes de thread de serveur dans le processus

Processus, threads et appartements

Communication à thread unique et multithread