为什么PerThreadLifetimeManager在此示例中使用?

时间:2013-01-29 20:14:21

标签: dependency-injection unity-container repository-pattern unit-of-work object-lifetime

我正在关注下面链接的示例,用于设置Unity以使用我的服务层。我的项目设置与本文中的项目非常相似,除了为什么在注册服务依赖项时使用PerThreadLifetimeManager时我理解了所有内容。请记住,我也在使用我的服务层中使用的通用存储库和单元工作。大多数统一示例使用默认(瞬态)生命周期管理器,因为我的设置类似于下面的设置,我想知道为什么我应该使用PerThreadLifeimeManager?我正在为我当前的表示层使用ASP.NET Web表单项目,如果它发生了任何变化。

container.RegisterType<ICatalogService, CatalogService>(new PerThreadLifetimeManager())

The repository pattern with EF code first dependency injection in asp.net MVC 3

1 个答案:

答案 0 :(得分:29)

每线程生活方式被认为是有害的

每线程生命周期是一种非常危险的生活方式,一般来说,在您的应用程序中使用它,尤其是Web应用程序。

这种生活方式应该被认为是危险的,因为很难预测线程的实际寿命。当您使用new Thread().Start()创建并启动线程时,您将获得一个新的线程静态内存块,这意味着容器将为您创建一个新的每线程实例。但是,使用ThreadPool.QueueUserWorkItem从线程池启动线程时,可能会从池中获得现有线程。在ASP.NET中运行时也是如此。 ASP.NET池线程以提高性能。

这意味着线程几乎总是比Web请求更长。另一方面,ASP.NET可以异步运行请求,这意味着Web请求可以在不同的线程上完成。使用Per Thread生活方式时,这是一个问题。当然,当你开始使用async / await时,这种效果会被放大。

这是一个问题,因为您通常会在请求开始时调用Resolve<T>一次。这将加载完整的对象图,包括使用Per Thread生活方式注册的服务。当ASP.NET在不同的线程上完成请求时,这意味着已解析的对象图移动到这个新线程,包括所有Per Thread注册的实例。

由于这些实例被注册为Per Thread,因此它们可能不适合在另一个线程中使用。它们几乎肯定不是线程安全的(否则它们将被注册为Singleton)。由于最初启动请求的第一个线程已经可以自由地获取新请求,因此我们可以遇到两个线程同时访问每个线程实例的情况。这将导致竞争条件和难以诊断和发现的错误。

所以一般来说,使用Per Thread是个坏主意。而是使用具有明确范围的生活方式(隐含或明确定义的开始和结束)。大多数DI框架实现的Per Web Request生活方式通常是隐含的范围(您不必自己结束)。

特定于您的问题

更糟糕的是,您引用的博客文章包含配置错误。 ICatalogService是使用 Per Thread 生活方式定义的。但是,此服务依赖于IDALContext服务,该服务定义为 Transient 。由于对IDALContext实例的引用存储为CatalogService内的私有字段,这意味着DALContext只要ICatalogService生效。这是一个问题,因为IDALContext被定义为 Transient ,并且可能不是线程安全的。

生活方式的一般规则

一般规则是让组件仅依赖具有相同或更长生命周期的服务。因此,瞬态可以依赖于单身人士而不是相反。

由于Per Thread注册的组件通常会存活很长时间,因此通常只能安全地依赖于其他Per Thread或Singleton实例。由于ASP.NET可以在多个线程中分割单个请求,因此在ASP.NET应用程序(MVC,Web窗体,尤其是Web API)的上下文中使用Per Thread是不安全的。