Quels sont les pièges pour rendre UnityContainer non thread-safe?

J’ajoute l’dependency injection à ma bibliothèque et j’utilise Unity pour cela. Et je me demande si je dois prendre des mesures supplémentaires pour rendre Unity Container thread-safe. J’ai trouvé quelques articles sur le conteneur Thread-safe (exemple: http://www.fascinatedwithsoftware.com/blog/post/2012/01/04/A-Thread-Safe-Global-Unity-Container.aspx ). mais je ne comprends pas si j’en ai vraiment besoin dans mon projet. D’un côté, je ne veux pas avoir de vilains bugs en raison des conditions de compétition de l’autre côté. Je ne vois pas dans quel cas la situation de compétition se produirait. Je veux utiliser le motif Unity avec une racine de composition et enregistrer tous les types dans le constructeur statique de la manière suivante:

internal static class ConfiguredUnityContainer { private static readonly UnityContainer Container = new UnityContainer(); static ConfiguredUnityContainer() { Container.RegisterType<IConnectionFactory>(); } public static T Resolve() { return Container.Resolve(); } } 

En gros, ma question est la suivante: dans quels cas ai-je besoin de thread-safe supplémentaire lorsque je travaille avec Unity DI? Où puis-je trouver des conditions de course ou des problèmes de sécurité du fil?

L’unité (et tous les conteneurs communs) est garantie (par leurs concepteurs) d’être thread-safe (ou du moins, en quelque sorte ) en cas de résolution parallèle sans enregistrement. En d’autres termes, tant que vous séparez la phase d’enregistrement de la phase de résolution et d’un point à partir du conteneur uniquement, vous pouvez appeler Resolve en parallèle à partir de plusieurs threads sans problème.

En fait, à titre de pratique exemplaire, vous devez toujours séparer ssortingctement la phase d’enregistrement de la phase de résolution, car cela entraînerait de graves problèmes et rendrait la recherche des conditions de course très difficile.

Cette séparation de ces phases est si importante que certaines bibliothèques DI (telles que Autofac et Simple Injector ) vous forcent à vous imposer ce modèle (où Simple Injector est le plus ssortingct des deux). La documentation de Simple Injector contient une explication très claire de la raison pour laquelle Simple Injector vous oblige à utiliser ce modèle et explique ce qui pourrait arriver si vous pouviez modifier la configuration. Pour citer une partie de cette explication ici (mais vous devriez certainement lire toute l’explication):

Des problèmes de sécurité des threads peuvent facilement apparaître lorsque l’utilisateur modifie un enregistrement lors d’une requête Web. Si le conteneur autorisait de telles modifications d’enregistrement pendant une requête, d’autres modifications pourraient être directement affectées par ces modifications (étant donné qu’en général, il ne devrait y avoir qu’une seule instance de Container par AppDomain). En fonction de choses telles que le mode de vie de l’enregistrement; l’utilisation des usines et la structure du graphe d’objects, il est fort possible qu’une autre requête obtienne l’ancien et le nouvel enregistrement. Prenons, par exemple, un enregistrement transitoire remplacé par un autre. Si cela est fait pendant la résolution d’un graphe d’object d’un thread différent et l’injection du service dans plusieurs points du graphe, le graphe contiendrait une instance différente de cette abstraction avec des durées de vie différentes au même moment dans la même requête, ce qui est mauvais.

D’après ce que je vois, l’article que vous liez entre davantage dans la différence entre l’ anti-pattern Service Locator et l’application de Dependency Injection correctement, ce qui signifie que vous devez uniquement accéder au conteneur situé dans votre racine de composition . L’auteur de cet article (Larry Spencer) n’est pas très clair, mais il crée un seul conteneur Unity dans sa racine de composition et l’utilise pendant toute la durée de l’application. Dans un sens, il est toujours ‘global’, mais il empêche l’access à cette instance via l’application (car il s’agit du modèle Service Locator).

Bien que l’auteur tente de créer une enveloppe thread-safe autour du conteneur Unity, sa tentative est naïve. Ce qu’il fait, c’est créer un verrou autour de chaque méthode de Register et de Resolve . Cela entraînera non seulement une énorme congestion dans les applications multithreads, mais ne résoudra pas le problème de ce qui se passe lors de l’enregistrement et du remplacement d’instances après la construction et la mise en cache des graphiques d’object, comme l’expliquent la documentation de Simple Injector et la question de Unity. .

Dans le cas d’une dependency injection, les considérations de sécurité des threads vont au-delà du niveau du conteneur. Le type de dépendance que vous enregistrez dans le conteneur a également une grande importance. Dans la plupart des cas, les dépendances que vous enregistrez dans le conteneur sont des singletons. Si votre conteneur est statique, il est global pour tous les threads. En d’autres termes, chaque fil aura access à la même instance singleton. Par conséquent, si la dépendance que vous enregistrez conserve un état (avec état), vous devez considérer que d’autres threads peuvent modifier cet état. Pour éviter ce genre de mal de tête:

1) Vous pouvez vous limiter à l’enregistrement de dépendances sans état.

2) Vous pouvez créer une instance d’unité [ThreadStatic]. Dans ce cas, chaque thread aura sa propre instance d’unité et les dépendances avec état seront moins problématiques.

3) La meilleure option consiste à utiliser PerThreadLifetimeManager de Unity pour les dépendances avec état. Ce qui garantira que chaque thread aura sa propre instance de dépendance.