本文介绍了何时使用PerThreadLifetimeManager?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在遵循下面链接的示例,以设置与我的服务层一起使用的统一性。我的项目的设置与本文中的非常相似,并且我了解所有内容,但为什么在注册服务依赖项时确切使用了PerThreadLifetimeManager。请记住,我还使用了在我的服务层中也使用的通用存储库和单元。大多数统一示例都使用默认(瞬态)生存期管理器,并且由于我的设置与下面的类似,所以我想知道为什么我应该使用PerThreadLifeimeManager?如果当前的显示层发生任何更改,我正在使用ASP.NET Web窗体项目。

I am following the example linked to below for setting up unity to work with my service layer. My project is setup very similar to the one in this article and I understand everything except why exactly is PerThreadLifetimeManager used when registering the service dependency. Keep in mind I am also using a generic repository and unitofwork that is being used in my service layer as well. Most unity examples use the default(transient) lifetime manager, and since my setup is similar to the one below I'm wondering why I should use the PerThreadLifeimeManager? I am using an ASP.NET web forms project for my current presentation layer if that changes anything.

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

推荐答案

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



每线程生存时间是一种非常危险的生活方式,通常,您不应不要在应用程序中使用它,尤其是Web应用程序。

Per Thread Lifestyle Considered Harmful

The Per Thread Lifetime is a very dangerous lifestyle and in general you should not use it in your application, especially web applications.

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

This lifestyle should be considered dangerous, because it is very hard to predict what the actual lifespan of a thread is. When you create and start a thread using new Thread().Start(), you'll get a fresh block of thread-static memory, which means the container will create a new per-threaded instance for you. When starting threads from the thread pool using ThreadPool.QueueUserWorkItem however, you get possibly an existing thread from the pool. The same holds when running in ASP.NET. ASP.NET pools threads to increase performance.

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

This means that a thread will almost always outlive a web request. ASP.NET on the other hand can run requests asynchronously, which means that a web request can be finished at a different thread. And this is a problem when working with a Per Thread lifestyle. And of course this effect is amplified when you start using async/await.

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

This is a problem since you will typically call Resolve<T> once at the beginning of the request. This will load the complete object graph including your services that are registered with the Per Thread lifestyle. When ASP.NET finishes the request at a different thread, this means that the resolved object graph moves to this new thread, including all Per Thread registered instances.

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

Since these instances are registered as Per Thread, they are probably not suited to be used at another thread. They are almost certainly not thread-safe (otherwise they would be registered as Singleton). Since the first thread that initially started the request is already free to pick up new requests, we can run into the situation where two threads access those Per Thread instances simultaneously. This will lead to race conditions and bugs that are hard to diagnose and find.

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

So in general, using Per Thread is a bad idea. Instead use a lifestyle that has a clear scope (an implicit or explicitly defined begin and end). The Per Web Request lifestyle that most DI frameworks implement is often implicitly scoped (you don't have to end it yourself).

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

To make things worse, the blog post you referenced contains a configuration error. The ICatalogService is defined with a Per Thread lifestyle. This service however depends on an IDALContext service, which is defined as Transient. Since a reference to a IDALContext instance is stored as private field inside the CatalogService, this means the DALContext lives as long as the ICatalogService does. This is a problem because IDALContext is defined as Transient and is probably not thread-safe.

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

The general rule is to let components only depend on services with an equal or longer lifetime. So a Transient can depend on a Singleton but not the other way around.

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

Since a Per Thread registered component will typically live very long, it can typically only safely depend on other Per Thread or Singleton instances. And since ASP.NET can split a single request up in multiple threads, it is not safe to use Per Thread in the context of a ASP.NET application (both MVC, Web Forms, and especially Web API).

这篇关于何时使用PerThreadLifetimeManager?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 09:43