Autofac多线程问题 [英] Autofac multithreading issues
问题描述
我正在尝试在我的应用程序中使用autofac DI.我创建了一个包装器类以抽象出所有autofac dll:
I'm trying to use autofac DI in my application. I created a wrapper class to abstract away all the autofac dlls:
FrameworkDependencyResolver:记录器,IFrameworkDependencyResolver
FrameworkDependencyResolver : Logger, IFrameworkDependencyResolver
在此类中,我持有容器构建器,并将所有依赖项注册到应用程序根目录中.注册我的类型后,我将其构建并保存容器:
In this class I hold the container builder, and register all my dependencies in the application root. After registering my types I build it and hold the container:
Icontainer _container; ContainerBuilder _builder
Icontainer _container; ContainerBuilder _builder
public FrameworkDependencyResolver()
{
_builder = new ContainerBuilder();
}
在我的应用程序深处,我想使用FrameworkDependencyResolver对象来解析协议并打开与外部应用程序的连接,因此我使用以下代码将该对象注册为IFrameworkDependencyResolver:
Deep in my application i want to use the FrameworkDependencyResolver object to resolve protocols and open connections to external applications, therefore I registered this object as IFrameworkDependencyResolver with the following code:
_builder.RegisterInstance(obj).As<T>();
Obj是FrameworkDependencyResolver,T是接口
Obj is thr FrameworkDependencyResolver, T is the interface
在我的启动线程中,我解析在其ctor中使用FrameworkDependencyResolver的对象,并且它可以正常工作,解析效果很好,但是当我解析在其ctor中采用FrameworkDependencyResolver的内层(在新线程上)时,解决我遇到死锁的已注册协议对象.
In my starter thread, I resolve object that takes the FrameworkDependencyResolver in his ctor, and it works perfectly, resolvings are fine, however when I resolve an inner layer(on new thread) that takes the FrameworkDependencyResolver in it's ctor and try to resolve a registered protocol object I face deadlock.
例如:
主要:
var rootResolver = new FrameworkDependencyResolver();
rootResolver.RegisterType<IClass3, Class3>(Lifecycles.Singleton);
rootResolver.RegisterType<IClass2, Class2>(Lifecycles.Singleton);
rootResolver.RegisterType<Container, TestContainer>(Lifecycles.Singleton);
rootResolver.RegisterObject<IFrameworkDependencyResolver, FrameworkDependencyResolver>(rootResolver);
rootResolver.BuildContainer();
rootResolver.Resolve<TestContainer>();
Console.ReadKey();
TestContainer代码:
TestContainer code:
public TestContainer(IFrameworkDependencyResolver resolver) : base(resolver){}
protected override void InitializeContainer()
{
_class2 = DependencyResolver.Resolve<IClass2>();
Thread.Sleep(20000);
Console.WriteLine("Container initialize finished");
}
Class2代码:
public class2(IFrameworkDependencyResolver resolver)
{
_resolver = resolver;
var thread = new Thread(startMethod);
thread.Start();
Console.WriteLine("Class2 ctor ended");
}
void StartMethod()
{
_class3 = _resolver.Resolve<IClass3>();
Console.WriteLine("Start method finished");
}
这个简单的示例程序的输出是:
The output of this simple example program is:
Class2 ctor ended
Container initialize ended
Start method finished
这意味着我创建的线程正在等待主线程完成,并且只能解决该问题.我想防止这种情况,并使其可以随时从每个线程进行解析.请帮助我了解造成这种情况的原因. 谢谢
Meaning that the thread I created is waiting for the main thread to finish and only than it can resolve. I want to prevent this and make it possible to resolve anytime from every thread. Please help me understand what is causing this. Thank you
这个问题没有解决,因为autofac从根范围解决了单例问题..我相信我的问题类似于此处描述的问题:
The problem is not solved because autofac resolves singletons from the root scope..I believe my problem is similar to the one described here : Autofac resolving a singleton creates a bottleneck but I don't really understand the solution
对于瓶颈问题,我了解到ctor根本不应该包含逻辑. 我还了解到我可能不应该绕过IFrameworkDependencyResolver对象,而应该使用Func<>.
Edit 2: for the bottleneck issue I learned that ctors should not contain logic at all. I also learned I probably shouldn't pass around my IFrameworkDependencyResolver object and should probably use Func<>.
我的应用程序结构: 我的应用程序中有一个层来处理连接请求,并且为每种请求创建一个不同类型的协议(一个不同的协议对象)
My application structure: I have a layer in my application that handles connection requests and for every kind of request creates a different kind of protocol (a different protocol object)
// For example lets say a protocol takes in ctor these 3 services + runtime configuration object:
public Protocol1(IFramingAgent, IFramingAlgorithm, IFramingParser, configObject configuration)
每个服务都注册有密钥,因为每种协议都使用不同的协议
Each service is registered with key because each protocol uses a different one
这是我的糟糕课:
public class ProtocolsLayer : Layer
{
private IFrameworkDependencyResolver _resolver;
private IConfigurationService _configService;
public ProtocolsLayer(IFrameworkDependencyResolver resolver, IConfigurationService configurationService)
{
_resolver = resolver;
_configService = configurationService;
}
void HandleConnection1()
{
// What I have at the moment (terrible):
// Resolve the fitting services (All keyed - key is received by the type, Resolve and ResolveWithParameters used here are my wrappers)
var agent = _resolver.Resolve<IFramingAgent>(typeof(Protocol1FramingAgent));
var algo = _resolver.Resolve<IFramingAlgorithm>(typeof(Protocol1FramingAlgorith));
var parser = _resolver.Resolve<IFramingParser>(typeof(Protocol1FramingParser));
// A parameter I get and pass to each protocol at runtime
var protocolConfig = _configService.GetConfig<Protocol1Configuration>();
// Finally resolve the protocol with it's parameters:
protocol = _resolver.ResolveWithParameters<IProtocol>(typeof(Protocol1), new List<object>{
agent, resolver, parser, protocolConfig
});
//...
// Theres gotta be a better way!!
}
void HandleConntection2()
{
// Same as in protocol1
}
void HandleConnection3()
{
// Same as in protocol1
}
}
请记住,我不希望引用autofac,这意味着我不能使用听到的IIndex<>.
Take in mind that I don't want references to autofac, meaning I can't use IIndex<> which I heard off.
谢谢!
推荐答案
我制作了一个示例来重现您的问题: https://dotnetfiddle.net/WOGwoD
I made a sample to reproduce your issue : https://dotnetfiddle.net/WOGwoD
如果我总结一下,您的问题是一次只能有一个线程的Autofac Resolve
.
If I summarize, your issue is that Autofac Resolve
for only thread at a time.
让另一个代码示例重现该问题:
Let take another code sample to reproduce the issue :
class Foo1
{
public Foo1()
{
Console.WriteLine("begin Foo1");
Thread.Sleep(1000);
Console.WriteLine("end Foo1");
}
}
class Foo2
{
public Foo2()
{
Console.WriteLine("begin Foo2");
Thread.Sleep(1000);
Console.WriteLine("end Foo2");
}
}
public class Program
{
public static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo1>().AsSelf().SingleInstance();
builder.RegisterType<Foo2>().AsSelf().SingleInstance();
IContainer container = builder.Build();
var t1 = Task.Run(() => container.Resolve<Foo1>());
var t2 = Task.Run(() => container.Resolve<Foo2>());
Task.WaitAll(t1, t2);
}
}
该程序的输出如下:
begin Foo1
end Foo1
begin Foo2
end Foo2
如果您为单个注册将生存期范围从SingleInstance
更改为InstancePerDependency
(默认值),输出将是:
If you change the lifetime scope from SingleInstance
to InstancePerDependency
(the default one) for a single registration, the output will be :
begin Foo1
begin Foo2
end Foo1
end Foo2
我们可以看到 Autofac 在激活 Shared 注册时将IContainer
锁定为 Shared 注册.
lock
语句为 LifetimeScope的258行. cs .
我认为此行为是为了防止出现复杂的依赖关系图问题.即:如果Foo1
对Foo2
具有嵌套依赖关系,会发生什么?
We can see that Autofac lock the IContainer
for Shared registration while it is activating a Shared registration.
The lock
statement is Line 258 of LifetimeScope.cs.
I think this behavior is here to prevent issue with complex dependency graph. ie : What happens if Foo1
has a nested dependency on Foo2
?
您将无法绕过Autofac
的这种行为.
You won't be able to bypass this behavior of Autofac
.
要更改此行为,您将需要更改代码的工作方式.构造函数并非要花时间.我建议您将构造函数更改为仅执行必需的操作,如果某些初始化过程需要花费时间,则我会推迟它或重构代码以确保构造函数仅花费几毫秒即可完成.
To change this behavior, you will need to change the way your code works. A constructor is not intended to take time. I recommend you to change your constructor to do only required things, if some of initialization process takes time I would defer it or refactor the code to ensure that constructor takes only few milliseconds to complete.
我创建了一个包装器类以抽象出所有autofac dll
I created a wrapper class to abstract away all the autofac dlls
您的核心代码不应依赖于依赖项注入组件.在您的情况下,您似乎使用IFrameworkDependencyResolver
接口来延迟加载组件或拥有工厂组件.您应该改为依赖Func<T>
的Lazy<T>
.有关更多信息,请参见隐式关系类型.
Your core code should not rely on dependency injection component. In your case, it looks like you use the IFrameworkDependencyResolver
interface to lazy load component or to have a factory component. You should rely on Lazy<T>
of Func<T>
instead. See implicit relation type for more information.
这篇关于Autofac多线程问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!