C# Autofac解决单例问题会造成瓶颈
我在asp.net MVC应用程序中使用Autofac时遇到了锁定问题。任何时候,只要一个服务依赖于一个单例,就会从根生存期范围解析该服务。这是因为Autofac:C# Autofac解决单例问题会造成瓶颈,c#,asp.net-mvc,autofac,C#,Asp.net Mvc,Autofac,我在asp.net MVC应用程序中使用Autofac时遇到了锁定问题。任何时候,只要一个服务依赖于一个单例,就会从根生存期范围解析该服务。这是因为Autofac: 从根作用域解析单例组件 解析根作用域中具有必须从根解析的依赖项的所有组件 此外,当从任何作用域解析时,Autofac会锁定该作用域。我认为这些都是很好的设计决策。我的问题是行为不端的类依赖于单例,并且的构造函数很慢。这为其他需要解决单例问题的人造成了瓶颈 因为这是在MVC应用程序中,所以每个请求都被映射到某个被注入构造函数的MV
- 从根作用域解析单例组件
- 解析根作用域中具有必须从根解析的依赖项的所有组件
//registered as SingleInstance()
class MySingleton {}
class BadTransient
{
public BadTransient(MySingleton s)
{ Thread.Sleep(5000); }
}
class FriendlyTransient {}
像
using(var scope = container.BeginLifetimeScope("nested scope"))
{
//resolves from child scope
var myFriend = scope.Resolve<FriendlyTransient>();
//resolves from root because it's a singleton
var singleton = scope.Resolve<MySingleton>();
//resolves from root because it has a singleton dependency.
//**** this locks the root scope for 5 seconds
//**** no one else can resolve singletons.
var troublemaker = scope.Resolve<BadTransient>();
}
使用(var scope=container.BeginLifetimeScope(“嵌套范围”))
{
//从子范围解析
var myFriend=scope.Resolve();
//从根解析,因为它是单例
var singleton=scope.Resolve();
//从根目录解析,因为它具有单例依赖项。
//****这会将根作用域锁定5秒
//****没有其他人可以解决单身问题。
var troublemaker=scope.Resolve();
}
有什么方法可以避免这个瓶颈吗?
显而易见的答案是拥有快速的构造函数。事实上,并不是我的代码库中的所有构造函数都能保证是快速的。有很多遗留代码,有很多代码依赖于第三方代码,有一些代码看起来很快,但依赖于不快的代码,有一些代码通常很快,但在奇怪的情况下会崩溃,等等。对开发人员的教育只在一定程度上起作用
我们一直在修复构造函数,因为我们发现他们,但我需要一个更积极的解决方案。让我的用户做我的QA是不可接受的
注意:我不太关心那些不依赖单例的慢构造函数。它们将锁定其生存期范围,但不会阻止其他线程我同意@nemesv的观点,即对象构造应该快速,但这也意味着不在激活的中初始化。相反,您应该在第一次使用组件时惰性地执行此操作。例如,通过在内部实现代理或一些
惰性
但如果您有一个具有相当极端吞吐量和并发特性的应用程序,并且通过性能分析验证锁定是一个瓶颈,那么您可以考虑切换到一个无锁的IOC容器。
< P>我通过使用AutoFAC的闭包语法重新注册所有的单体来修复这个问题。
这将在autofac中保留构造逻辑,但从子生命周期范围解析单例。实质上:
builder.Register<MySingleton>().AsSelf().AsImplementedInterfaces();
//.. other registrations
var container = builder.Build();
// now resolve each singleton, forcing all to be constructed if not already
// and then register the instance
var builder2 = new ContainerBuilder();
var mySingleton = container.Resolve<MySingleton>();
builder2.Register(c => mySingleton).AsSelf().AsImplementedInterfaces();
builder2.Update(container);
//.....
var scope = container.BeginLifetimeScope("child scope");
scope.Resolve<MySingleton>(); //not resolved from root!
builder.Register();
//.. 其他注册
var container=builder.Build();
//现在解析每个单例,如果尚未构造,则强制构造所有单例
//然后注册实例
var builder2=新的ContainerBuilder();
var mySingleton=container.Resolve();
builder2.Register(c=>mySingleton.AsSelf().AsImplementedInterfaces();
builder2.更新(容器);
//.....
var scope=container.BeginLifetimeScope(“子范围”);
scope.Resolve()//不是从根本上解决的!
然后,由于有许多单例,我可以通过编程方式查询它们的类型,因此我编写了一个函数,该函数接受类型列表并运行上述代码。它必须执行一些反射向导,尽管只在应用程序启动时在正常的autofac注册码末尾运行
void ReRegisterSingletons(IContainer container, List<Type> singletonTypes)
{
var builder= new ContainerBuilder();
foreach(var type in singletonTypes)
{
var resolved = container.Resolve(type);
var method = this.GetType().GetMethod("ReRegister").MakeGenericMethod(new []{type});
method.Invoke(this, new []{resolved});
}
builder.Update(container);
}
void Register<T>(ContainerBuilder builder, object singleton)
{
var theObj = (T)singleton;
//a typed lambda was the only way I could get both the class name and the interface names to resolve from the child scope. RegisterInstance still resolves from root, and the non-generic lamba register can resolve the class name from child scope but not the interface names...
builder.Register(c => theObj).AsSelf().AsImplementedInterfaces();
}
void重新注册Singleton(IContainer容器,列出Singleton类型)
{
var builder=new ContainerBuilder();
foreach(singletonTypes中的变量类型)
{
var resolved=container.Resolve(类型);
var method=this.GetType().GetMethod(“重新注册”).MakeGenericMethod(新[]{type});
调用(这是新的[]{resolved});
}
生成器更新(容器);
}
无效寄存器(ContainerBuilder生成器,对象单例)
{
var-theObj=(T)单态;
//类型化lambda是我从子作用域获取要解析的类名和接口名的唯一方法。RegisterInstance仍然从根解析,非泛型lamba寄存器可以从子作用域解析类名,但不能解析接口名。。。
builder.Register(c=>theObj.AsSelf().asiimplementedinterfaces();
}
我认为Autofac的工作方式没有任何问题。这里我看到的问题是,您有一些badtransive
类,其中构造函数中有昂贵的逻辑。我将把所有的逻辑从构造函数移到一个Initialize
方法,这个方法可以被AutofacOnActivated
事件调用。因此,在整个初始化过程中不需要锁定作用域。@nemesv请查看我的编辑,请查看我的编辑。我喜欢使用惰性慢构造函数的想法,但我正在寻找一种更全面的解决方案——假设我无法保证所有构造函数都是快速的,那么如何保持应用程序的响应性呢?您推荐任何无锁容器吗?您当然可以将所有单例注册为瞬态。如果我没弄错的话,这会阻止全局锁定。我知道的唯一(好的)无锁容器是(我是SimpleInjector的首席开发人员)。关于简单喷油器的一个警告。它有一套非常有限的精心挑选的功能。这种设计试图引导开发者进入成功的深渊。但既然你是d