使UnityContainer不是线程安全的陷阱是什么?

时间:2015-05-27 19:24:08

标签: c# dependency-injection unity-container

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

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时,在什么情况下我还需要额外的线程安全?我在哪里可以获得有关线程安全的竞争条件或问题?

2 个答案:

答案 0 :(得分:9)

Unity(以及所有常见容器)在没有注册的并行解析的情况下(由其设计者)保证是线程安全的(或者至少是sort of)。换句话说,只要您将注册阶段与解析阶段分开,并且从仅从容器解析的一个点开始,就可以从多个线程并行调用Resolve而没有问题。

事实上,作为一种最佳做法,你应该始终严格地将登记阶段与解决阶段分开,因为这将导致严重的麻烦并且很难找到竞争条件。

这些阶段的分离是如此重要,以至于某些DI库(例如AutofacSimple Injector)会强制使用此模式(其中Simple Injector是两者中最严格的)。 Simple Injector文档包含一个very clear explanation,说明为什么Simple Injector强迫您使用此模型,并解释在您能够更改配置时可能发生的情况。在这里引用部分解释(但你一定要阅读整个解释):

  

当用户更改时,很容易出现线程安全问题   在Web请求期间注册。如果容器允许这样的话   在请求期间注册更改,其他请求可以直接   受这些变化的影响(因为一般来说应该只有   每个AppDomain一个Container实例)。取决于诸如此类的东西   注册的生活方式;工厂的使用和对象如何   图形是结构化的,它可能是另一个的真实可能性   请求获得旧注册和新注册。举个例子   临时注册,替换为不同的注册。如果   这是在一个不同线程的对象图正在进行时完成的   服务被注入到内部的多个点时解决了   graph - 图形将包含该抽象的不同实例   在同一个请求中同时使用不同的生命周期 - 和   这很糟糕。

正如我所看到的那样,您正在链接的the article更多地考虑了Service Locator anti-pattern与正确应用依赖注入之间的差异,这意味着只访问Composition Root内的容器。该文章的作者(拉里斯宾塞)不是很清楚,但在他的作品根中,他创建了一个单独的Unity容器,并在整个应用程序的持续时间内使用它。从某种意义上说,它仍然是“全局的”,但他阻止通过应用程序访问该实例(因为这是服务定位器模式)。

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

答案 1 :(得分:3)

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

1)您可以限制自己注册无状态的依赖项。

2)你可以创建一个[ThreadStatic]统一实例。在这种情况下,每个线程将拥有自己的统一实例,并且有状态依赖性将不再是一个问题。

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