何时使用PerThreadLifetimeManager?

问题描述 投票:11回答:1

我正在遵循下面链接的示例,以设置与我的服务层一起使用的统一性。我的项目的设置与本文中的非常相似,并且我了解所有内容,但为什么注册服务依赖项时确实使用了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

dependency-injection unity-container repository-pattern unit-of-work object-lifetime
1个回答
30
投票

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

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

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

这意味着线程几乎总是会超过Web请求的寿命。另一方面,ASP.NET可以异步运行请求,这意味着Web请求可以在其他线程中完成。这是每线程生活方式下的一个问题。当然,当您开始使用异步/等待时,这种影响会放大。

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

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

因此,一般而言,使用“每线程”是一个坏主意。而是使用范围明确的生活方式(隐式或显式定义的开始和结束)。大多数DI框架实现的“按Web请求”生活方式通常是隐式作用域的(您不必自己结束)。

针对您的问题

为了使情况更糟,您引用的博客文章包含配置错误。 ICatalogService通过Per Thread生活方式定义。但是,此服务取决于IDALContext服务,该服务定义为Transient。由于对IDALContext实例的引用作为私有字段存储在CatalogService中,因此这意味着DALContext的生存时间与ICatalogService一样长。这是一个问题,因为IDALContext被定义为Transient,并且可能不是线程安全的。

生活方式的一般规则

一般规则是让组件仅依赖具有相等或更长寿命的服务。因此,瞬态可以依赖于Singleton,而不是相反。

由于每个线程注册的组件通常寿命很长,因此通常只能安全地依赖于其他每个线程或Singleton实例。而且由于ASP.NET可以将单个请求拆分为多个线程,所以在ASP.NET应用程序(MVC,Web窗体,尤其是Web API)的上下文中使用“每线程”是不安全的。

© www.soinside.com 2019 - 2024. All rights reserved.