2015-05-27 52 views
5

我正在向库中添加依赖注入,我使用Unity。 我想知道是否需要采取一些额外步骤使Unity Container线程安全。我发现了几篇讨论线程安全容器的文章(例如:http://www.fascinatedwithsoftware.com/blog/post/2012/01/04/A-Thread-Safe-Global-Unity-Container.aspx),但我不明白在我的项目中是否真的需要它。从另一方面来说,我不想因为另一方面的竞争条件而产生一些令人讨厌的错误,我不知道在什么情况下会出现竞争状况。我想用团结与成分根模式和静态构造函数一样,注册的所有种类:什么是使UnityContainer不是线程安全的缺陷?

internal static class ConfiguredUnityContainer 
    { 
     private static readonly UnityContainer Container = new UnityContainer(); 

     static ConfiguredUnityContainer() 
     { 
      Container.RegisterType<IConnectionFactory<SqlConnection>>(); 
     } 

     public static T Resolve<T>() 
     { 
      return Container.Resolve<T>(); 
     } 
    } 

所以,基本上我的问题是:在什么情况下,我需要更多的线程安全的,当我一起工作Unity DI?我在哪里可以获得线程安全的竞争条件或问题?

回答

8

如果没有注册并行解析,Unity(和所有常用容器)由其设计者保证是线程安全的(或至少是sort of)。换句话说,只要您将注册阶段从解决阶段和仅从容器中解析出的一个阶段分开,您可以从多个线程并行调用Resolve,而不会出现任何问题。

事实上,作为一种最佳实践,您应该始终严格区分注册阶段和解决阶段,因为这会导致严重的麻烦并且很难找到竞争条件。

这些阶段的这种分离是非常重要的,有些DI库(如AutofacSimple Injector)在你强迫这种模式(这里简单的注射器是最严格的两个)。简单注射器文档包含一个very clear explanation,说明为什么简单注射器强制您使用该模型,并解释如果您能够更改配置会发生什么情况。在这里引用的那部分解释(但你一定要阅读所有的解释):与线程安全

问题当用户在Web请求期间改变 登记容易出现。如果容器在请求期间允许注册更改 ,则其他请求可能会直接受到这些更改的影响(因为通常每个AppDomain应该只有一个Container实例,即 )。取决于诸如 注册的生活方式;工厂的使用以及图表的结构如何构成,可能会有另一个 请求同时获得旧注册和新注册。以 为例,用一个不同的替换的临时注册。如果 这样做,而不同的线程的对象图正在被解析,而服务被注入到图的多个点中 - 该图将包含该抽象的不同实例 ,同一时间具有不同的生命期要求 - 和 这是不好的。

在我看来,那the article要链接去多进Service Locator anti-pattern和应用依赖注入的区别正确,这意味着只有访问您的Composition Root容器内部。那篇文章的作者(Larry Spencer)并不十分清楚,但是在他的Composition Root中,他创建了一个单一的Unity容器并在整个应用程序中使用它。从某种意义上说,它仍然是'全局'的,但他阻止通过应用程序访问该实例(因为这是服务定位器模式)。

虽然编写者试图围绕Unity容器创建一个线程安全的包装器,但他的尝试是天真的。他所做的是在每个RegisterResolve方法周围创建一个锁。这不仅会在多线程应用程序中造成巨大的拥塞,而且也无法解决在已经构建和缓存对象图之后注册和替换实例时会发生什么问题,因为简单注入器文档解释了这一点,并且Unity question显示。

+2

这个答案很棒,这就是我一直在寻找的!谢谢! – vmg

3

在依赖注入的情况下,线程安全注意事项比容器级别更深层次。您注册到容器中的依赖类型也具有重要意义。在大多数情况下,您在Container中注册的依赖项是单例。如果你的容器是静态的,那么它对所有线程都是全局的。换句话说,每个线程都可以访问相同的单例实例。因此,如果您正在注册的依赖项维护一个状态(有状态),那么您需要考虑其他线程可能会更改该状态。为了避免这种头痛:

1)你可以限制自己注册无状态的依赖关系。

2)您可以创建一个[ThreadStatic]统一实例。在这种情况下,每个线程都有它自己的统一实例,并且有状态的依赖关系不会成为问题。

3)最好的选择是使用Unity的PerThreadLifetimeManager进行有状态的依赖关系。这将保证每个线程都有自己的依赖实例。

+1

我通常不会建议每个线程有一个容器,因为这会使事情变得非常复杂,但我完全同意使注册组件成为无状态单例。这让你的生活变得更简单。 +1 – Steven

相关问题