Autofac 多线程问题
Autofac multithreading issues
我正在尝试在我的应用程序中使用 autofac DI。我创建了一个包装器 class 来抽象所有 autofac dll:
FrameworkDependencyResolver:记录器,IFrameworkDependencyResolver
在这个 class 中,我持有容器生成器,并在应用程序根目录中注册我所有的依赖项。注册我的类型后,我构建它并保存容器:
图标容器_容器;
ContainerBuilder _builder
public FrameworkDependencyResolver()
{
_builder = new ContainerBuilder();
}
在我的应用程序中,我想使用 FrameworkDependencyResolver 对象来解析协议并打开与外部应用程序的连接,因此我使用以下代码将此对象注册为 IFrameworkDependencyResolver:
_builder.RegisterInstance(obj).As<T>();
Obj 是 thr FrameworkDependencyResolver,T 是接口
在我的起始线程中,我解析了在他的构造函数中采用 FrameworkDependencyResolver 的对象,它工作得很好,解析很好,但是当我解析一个内层(在新线程上)在它的构造函数中采用 FrameworkDependencyResolver 并且尝试解决我遇到死锁的已注册协议对象。
示例:
主要内容:
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();
测试容器代码:
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");
}
这个简单示例程序的输出是:
Class2 ctor ended
Container initialize ended
Start method finished
这意味着我创建的线程正在等待主线程完成并且只有它可以解析。我想防止这种情况,并可以随时从每个线程中解决。请帮助我了解造成这种情况的原因。
谢谢
编辑:
问题没有解决,因为 autofac 从根范围解析单例。我相信我的问题类似于这里描述的问题:Autofac resolving a singleton creates a bottleneck
但我不太明白解决方案
编辑 2:
对于瓶颈问题,我了解到 ctors 根本不应该包含逻辑。
我还了解到我可能不应该传递我的 IFrameworkDependencyResolver 对象,而应该使用 Func<>.
我的申请结构:
我的应用程序中有一个处理连接请求的层,并且为每种请求创建不同类型的协议(不同的协议对象)
// For example lets say a protocol takes in ctor these 3 services + runtime configuration object:
public Protocol1(IFramingAgent, IFramingAlgorithm, IFramingParser, configObject configuration)
每个服务都使用密钥注册,因为每个协议使用不同的密钥
这是我最糟糕的 class:
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<>。
谢谢!
我在我的应用程序中遵循相同的策略,用我的 classes 包装 DI 库,以便以后需要时可以更改它。
我采用了相同的方法,只有一点不同
在您的代码中,您在 class 构造函数中创建 ContainerBuilder
并继续引用它,这就是问题所在
相反,您可能需要移除 ContainerBuilder
实例,并且仅依赖 Autofac.ILifetimeScope
作为您的 FrameworkDependencyResolver
的构造函数依赖项,此依赖项将由 autofac 运行时注入正确的生命周期范围。
然后在您的代码的任何级别,您可以根据需要 FrameworkDependencyResolver
编辑
在我看到你的更新后,我建议你将服务的注册与解析实例分开,即像 FrameworkDependencyRegister
一样创建新的 class 并保留另一个用于解析并按照步骤操作上面的回答
在我看来抽象注册可能是太多不必要的抽象,你可以只写一个方法来使用普通的 autofac API 来完成这些事情
我制作了一个示例来重现您的问题:https://dotnetfiddle.net/WOGwoD
如果我总结一下,您的问题是 Autofac Resolve
一次仅用于线程。
让我们使用另一个代码示例来重现该问题:
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
(默认值),输出将是:
begin Foo1
begin Foo2
end Foo1
end Foo2
我们可以看到 Autofac 在激活 时为 Shared 注册锁定 IContainer
共享 注册。
lock
语句是 Line 258 of LifetimeScope.cs。
我认为这种行为是为了防止复杂的依赖关系图出现问题。即:如果 Foo1
对 Foo2
具有嵌套依赖性,会发生什么情况?
您将无法绕过 Autofac
的这种行为。
要更改此行为,您需要更改代码的工作方式。构造函数不打算花费时间。我建议你改变你的构造函数只做需要的事情,如果一些初始化过程需要时间我会推迟它或重构代码以确保构造函数只需几毫秒即可完成。
I created a wrapper class to abstract away all the autofac dlls
您的核心代码不应依赖依赖注入组件。在您的情况下,您似乎使用 IFrameworkDependencyResolver
接口来延迟加载组件或拥有工厂组件。您应该改为依赖 Lazy<T>
of Func<T>
。有关详细信息,请参阅 implicit relation type。
我正在尝试在我的应用程序中使用 autofac DI。我创建了一个包装器 class 来抽象所有 autofac dll:
FrameworkDependencyResolver:记录器,IFrameworkDependencyResolver
在这个 class 中,我持有容器生成器,并在应用程序根目录中注册我所有的依赖项。注册我的类型后,我构建它并保存容器:
图标容器_容器; ContainerBuilder _builder
public FrameworkDependencyResolver()
{
_builder = new ContainerBuilder();
}
在我的应用程序中,我想使用 FrameworkDependencyResolver 对象来解析协议并打开与外部应用程序的连接,因此我使用以下代码将此对象注册为 IFrameworkDependencyResolver:
_builder.RegisterInstance(obj).As<T>();
Obj 是 thr FrameworkDependencyResolver,T 是接口
在我的起始线程中,我解析了在他的构造函数中采用 FrameworkDependencyResolver 的对象,它工作得很好,解析很好,但是当我解析一个内层(在新线程上)在它的构造函数中采用 FrameworkDependencyResolver 并且尝试解决我遇到死锁的已注册协议对象。
示例:
主要内容:
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();
测试容器代码:
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");
}
这个简单示例程序的输出是:
Class2 ctor ended
Container initialize ended
Start method finished
这意味着我创建的线程正在等待主线程完成并且只有它可以解析。我想防止这种情况,并可以随时从每个线程中解决。请帮助我了解造成这种情况的原因。 谢谢
编辑: 问题没有解决,因为 autofac 从根范围解析单例。我相信我的问题类似于这里描述的问题:Autofac resolving a singleton creates a bottleneck 但我不太明白解决方案
编辑 2: 对于瓶颈问题,我了解到 ctors 根本不应该包含逻辑。 我还了解到我可能不应该传递我的 IFrameworkDependencyResolver 对象,而应该使用 Func<>.
我的申请结构: 我的应用程序中有一个处理连接请求的层,并且为每种请求创建不同类型的协议(不同的协议对象)
// For example lets say a protocol takes in ctor these 3 services + runtime configuration object:
public Protocol1(IFramingAgent, IFramingAlgorithm, IFramingParser, configObject configuration)
每个服务都使用密钥注册,因为每个协议使用不同的密钥
这是我最糟糕的 class:
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<>。
谢谢!
我在我的应用程序中遵循相同的策略,用我的 classes 包装 DI 库,以便以后需要时可以更改它。
我采用了相同的方法,只有一点不同
在您的代码中,您在 class 构造函数中创建 ContainerBuilder
并继续引用它,这就是问题所在
相反,您可能需要移除 ContainerBuilder
实例,并且仅依赖 Autofac.ILifetimeScope
作为您的 FrameworkDependencyResolver
的构造函数依赖项,此依赖项将由 autofac 运行时注入正确的生命周期范围。
然后在您的代码的任何级别,您可以根据需要 FrameworkDependencyResolver
编辑
在我看到你的更新后,我建议你将服务的注册与解析实例分开,即像 FrameworkDependencyRegister
一样创建新的 class 并保留另一个用于解析并按照步骤操作上面的回答
在我看来抽象注册可能是太多不必要的抽象,你可以只写一个方法来使用普通的 autofac API 来完成这些事情
我制作了一个示例来重现您的问题:https://dotnetfiddle.net/WOGwoD
如果我总结一下,您的问题是 Autofac Resolve
一次仅用于线程。
让我们使用另一个代码示例来重现该问题:
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
(默认值),输出将是:
begin Foo1
begin Foo2
end Foo1
end Foo2
我们可以看到 Autofac 在激活 时为 Shared 注册锁定 IContainer
共享 注册。
lock
语句是 Line 258 of LifetimeScope.cs。
我认为这种行为是为了防止复杂的依赖关系图出现问题。即:如果 Foo1
对 Foo2
具有嵌套依赖性,会发生什么情况?
您将无法绕过 Autofac
的这种行为。
要更改此行为,您需要更改代码的工作方式。构造函数不打算花费时间。我建议你改变你的构造函数只做需要的事情,如果一些初始化过程需要时间我会推迟它或重构代码以确保构造函数只需几毫秒即可完成。
I created a wrapper class to abstract away all the autofac dlls
您的核心代码不应依赖依赖注入组件。在您的情况下,您似乎使用 IFrameworkDependencyResolver
接口来延迟加载组件或拥有工厂组件。您应该改为依赖 Lazy<T>
of Func<T>
。有关详细信息,请参阅 implicit relation type。