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 是不安全的。