Problèmes de thread de serveur In-Process

Un serveur in-process n’appelle pas CoInitialize, CoInitializeEx ou OleInitialize pour marquer son modèle de threading. Pour les objets dll ou in-process prenant en charge les threads, vous devez définir le modèle de thread dans le Registre. Le modèle par défaut lorsque vous ne spécifiez pas de modèle de thread est un thread unique par processus. Pour spécifier un modèle, vous ajoutez la valeur ThreadingModel à la clé InprocServer32 dans le Registre.

Les DLL qui prennent en charge l’instanciation d’un objet de classe doivent implémenter et exporter les fonctions DllGetClassObject et DllCanUnloadNow. Lorsqu’un client souhaite un instance de la classe prise en charge par la DLL, un appel à CoGetClassObject (directement ou par le biais d’un appel à CoCreateInstance) appelle DllGetClassObject pour obtenir un pointeur vers son objet de classe lorsque l’objet est implémenté dans une DLL. DllGetClassObject doit donc être en mesure de donner plusieurs objets de classe ou un seul objet thread-safe (essentiellement en utilisant simplement InterlockedIncrement/InterlockedDecrement sur leurs nombres de références internes).

Comme son nom l’indique, DllCanUnloadNow est appelé pour déterminer si la DLL qui l’implémente est en cours d’utilisation, ce qui permet à l’appelant de la décharger en toute sécurité si ce n’est pas le cas. Les appels à CoFreeUnusedLibraries à partir de n’importe quel thread sont toujours acheminés par le main thread de l’appartement pour appeler DllCanUnloadNow.

Comme d’autres serveurs, les serveurs in-process peuvent être à thread unique, en appartement ou à thread libre. Ces serveurs peuvent être utilisés par n’importe quel client OLE, quel que soit le modèle de thread utilisé par ce client.

Toutes les combinaisons d’interopérabilité du modèle de threading sont autorisées entre les clients et les objets in-process. L’interaction entre un client et un objet in-process qui utilisent différents modèles de thread est exactement comme l’interaction entre les clients et les serveurs hors processus. Pour un serveur in-process, lorsque le modèle de thread du client et du serveur in-process diffèrent, COM doit s’interposer entre le client et l’objet.

Lorsqu’un objet in-process qui prend en charge le modèle monothread est appelé simultanément par plusieurs threads d’un client, COM ne peut pas autoriser les threads clients à accéder directement à l’interface de l’objet. L’objet n’a pas été conçu pour cet accès. Au lieu de cela, COM doit s’assurer que les appels sont synchronisés et sont effectués uniquement par le thread client qui a créé l’objet. Par conséquent, COM crée l’objet dans l’appartement main du client et exige que tous les autres appartements clients accèdent à l’objet à l’aide de proxys.

Lorsqu’un appartement à threads libres (modèle d’appartement multithread) dans un client crée un serveur in-process avec threads d’appartement, COM lance un thread « hôte » de modèle d’appartement à thread unique dans le client. Ce thread hôte crée l’objet et le pointeur d’interface est marshalé vers l’appartement à thread libre du client. De même, lorsqu’un appartement à thread unique dans un client de modèle d’appartement crée un serveur in-process avec thread libre, COM lance un thread hôte à thread libre (appartement multithread sur lequel l’objet sera créé, puis marshalé dans l’appartement monothread client).

Notes

En général, si vous concevez une interface personnalisée sur un serveur in-process, vous devez également fournir le code de marshaling pour celui-ci afin que COM puisse marshaler l’interface entre les appartements clients.

 

COM permet de protéger l’accès aux objets fournis par une DLL à thread unique en exigeant un accès à partir de l’appartement client dans lequel ils ont été créés. En outre, tous les points d’entrée de DLL (comme DllGetClassObject et DllCanUnloadNow) et les données globales doivent toujours être accessibles par le même appartement. COM crée ces objets dans l’appartement main du client, ce qui donne au main’appartement un accès direct aux pointeurs de l’objet. Les appels des autres appartements utilisent le marshaling interthread pour passer du proxy au stub dans le main appartement, puis à l’objet. Cela permet à COM de synchroniser les appels à l’objet . Les appels interthread étant lents, il est recommandé de réécrire ces serveurs pour prendre en charge plusieurs appartements.

À l’instar d’un serveur in-process à thread unique, un objet fourni par une DLL de modèle d’appartement doit être accessible par l’appartement client à partir duquel il a été créé. Toutefois, les objets fournis par ce serveur peuvent être créés dans plusieurs appartements du client. Le serveur doit donc implémenter ses points d’entrée (tels que DllGetClassObject et DllCanUnloadNow) pour une utilisation multithread. Par exemple, si deux appartements d’un client tentent de créer simultanément deux instances de l’objet in-process, DllGetClassObject peut être appelé simultanément par les deux appartements. DllCanUnloadNow doit être écrit pour que la DLL ne se décharge pas pendant que le code est toujours en cours d’exécution dans la DLL.

Si la DLL fournit une seule instance de la fabrique de classes pour créer tous les objets, l’implémentation de la fabrique de classe doit également être conçue pour une utilisation multithread, car elle sera accessible par plusieurs appartements clients. Si la DLL crée une nouvelle instance de la fabrique de classes chaque fois que DllGetClassObject est appelé, la fabrique de classes n’a pas besoin d’être thread-safe.

Les objets créés par la fabrique de classe n’ont pas besoin d’être thread-safe. Une fois créé par un thread, l’objet est toujours accessible via ce thread et tous les appels à l’objet sont synchronisés par COM. L’appartement de modèle d’appartement d’un client qui crée cet objet obtient un pointeur direct vers l’objet . Les appartements clients qui sont différents de l’appartement dans lequel l’objet a été créé doivent accéder à l’objet via des proxys. Ces proxys sont créés lorsque le client marshale l’interface entre ses appartements.

Quand une valeur DLL ThreadingModel in-process est définie sur « Both », un objet fourni par cette DLL peut être créé et utilisé directement (sans proxy) dans les appartements clients monothreads ou multithreads. Toutefois, il ne peut être utilisé directement que dans l’appartement dans lequel il a été créé. Pour donner l’objet à un autre appartement, l’objet doit être marshalé. L’objet DLL doit implémenter sa propre synchronisation et est accessible par plusieurs appartements clients en même temps.

Pour accélérer les performances de l’accès à thread libre aux objets DLL in-process, COM fournit la fonction CoCreateFreeThreadedMarshaler . Cette fonction crée un objet de marshaling à thread libre qui peut être agrégé avec un objet serveur in-process. Lorsqu’un appartement client dans le même processus a besoin d’accéder à un objet dans un autre appartement, l’agrégation du marshaleur à thread libre fournit au client un pointeur direct vers l’objet serveur, plutôt que vers un proxy, lorsque le client marshale l’interface de l’objet vers un autre appartement. Le client n’a pas besoin d’effectuer de synchronisation. Cela fonctionne uniquement dans le même processus ; Le marshaling standard est utilisé pour une référence à l’objet envoyé à un autre processus.

Un objet fourni par une DLL in-process qui prend uniquement en charge le thread libre est un objet à thread libre. Il implémente sa propre synchronisation et est accessible par plusieurs threads clients en même temps. Ce serveur ne marshale pas les interfaces entre les threads, de sorte que ce serveur peut être créé et utilisé directement (sans proxy) uniquement par des appartements multithreads dans un client. Les appartements à thread unique qui le créent y accèdent via un proxy.

Accès aux interfaces entre les appartements

Choix du modèle de thread

Appartements multithread

Processus, threads et appartements

Communication monothread et multithread

Appartements monothreads