12

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

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

[在 asp.net MVC 3 中使用 EF 代码优先依赖注入的存储库模式][1] [1]:http://www.dotnetage.com/publishing/home/2011/07/05/6883/the-repository-模式-with-ef-code-first-dependeny-injection-in-asp-net-mvc3.html

4

1 回答 1

32

Per Thread Lifetime 是一种非常危险的生活方式,一般来说你应该在你的应用程序中使用它,尤其是 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,因此它们可能不适合在另一个线程中使用。它们几乎肯定不是线程安全的(否则它们将被注册为单例)。由于最初启动请求的第一个线程已经可以自由接收新请求,因此您可能会遇到两个线程同时访问这些 Per Thread 实例的情况。这将导致难以诊断和发现的竞争条件和错误。

所以总的来说,使用 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 是不安全的。

于 2013-01-29T21:14:16.837 回答