C# Autofac多线程问题

C# Autofac多线程问题,c#,dependency-injection,autofac,deadlock,C#,Dependency Injection,Autofac,Deadlock,我正在尝试在我的应用程序中使用autofac DI。我创建了一个包装类来抽象掉所有autofac DLL: FrameworkDependencyResolver:记录器,IFrameworkDependencyResolver 在这个类中,我持有容器生成器,并在应用程序根目录中注册所有依赖项。注册我的类型后,我构建它并保存容器: 容器(i容器);; 集装箱建造商 public FrameworkDependencyResolver() { _builder = new ContainerB

我正在尝试在我的应用程序中使用autofac DI。我创建了一个包装类来抽象掉所有autofac DLL:

FrameworkDependencyResolver:记录器,IFrameworkDependencyResolver

在这个类中,我持有容器生成器,并在应用程序根目录中注册所有依赖项。注册我的类型后,我构建它并保存容器:

容器(i容器);; 集装箱建造商

public FrameworkDependencyResolver()
{
  _builder = new ContainerBuilder();
}
在我的应用程序中,我希望使用FrameworkDependencyResolver对象解析协议并打开与外部应用程序的连接,因此我使用以下代码将此对象注册为IFrameworkDependencyResolver:

_builder.RegisterInstance(obj).As<T>();
这意味着我创建的线程正在等待主线程完成,并且只能在它无法解决的情况下完成。我想防止这种情况,并使它有可能解决任何时候从每个线程。请帮助我了解这是什么原因造成的。 多谢各位

编辑: 这个问题没有解决,因为autofac从根作用域解析单例。我相信我的问题与这里描述的问题类似: 但我真的不明白解决办法

编辑2: 对于瓶颈问题,我了解到CTOR根本不应该包含逻辑。 我还了解到,我可能不应该传递IFrameworkDependencyResolver对象,而应该使用Func

我的应用程序结构: 我的应用程序中有一个层处理连接请求,对于每一种请求都创建一种不同的协议(不同的协议对象)

每个服务都使用密钥注册,因为每个协议使用不同的密钥

这是我糟糕的课堂:

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
    }
}
public类ProtocolsLayer:Layer
{
专用IFrameworkDependencyResolver\u解析器;
专用IConfigurationService\u配置服务;
公共协议杀手(IFrameworkDependencyResolver解析器,IConfigurationService配置服务)
{
_分解器=分解器;
_configService=configurationService;
}
void HandleConnection1()
{
//我目前的情况(糟糕):
//解析fitting services(所有keyed-key都由type接收,这里使用的Resolve和ResolveWithParameters是我的包装器)
变量代理=_resolver.Resolve(typeof(Protocol1FramingAgent));
var algo=_resolver.Resolve(typeof(Protocol1FramingAlgorith));
var parser=_resolver.Resolve(typeof(Protocol1FramingParser));
//我在运行时获取并传递给每个协议的参数
var protocolConfig=_configService.GetConfig();
//最后,使用其参数解析协议:
protocol=_resolver.ResolveWithParameters(typeof(Protocol1),新列表{
代理,解析器,解析器,protocolConfig
});
//...
//一定有更好的办法!!
}
void HandleConntection2()
{
//与协议1相同
}
void HandleConnection3()
{
//与协议1相同
}
}
请记住,我不想引用autofac,这意味着我不能使用我听说过的IIndex


谢谢

我在我的应用程序中遵循相同的策略,用我的类包装DI库,以便在以后需要时能够对其进行更改

我采用了同样的方法,只有一个区别 在代码中,您在类构造函数中创建了
ContainerBuilder
,并保持对它的引用,这就是问题所在

相反,您可能需要删除
ContainerBuilder
实例,只需依赖
Autofac.ILifetimeScope
作为
FrameworkDependencyResolver
的构造函数依赖项,此依赖项将由Autofac运行时以正确的生存期范围注入。 然后,在代码的任何级别,您都可以根据需要依赖
FrameworkDependencyResolver

编辑

在我看到您的更新后,我建议您将服务的注册与解析实例分开,即创建新类,如
FrameworkDependencyRegister
,并保留另一个用于解析,然后按照上面的步骤进行操作
在我看来,抽象注册可能是太多不必要的抽象,您可以编写一种方法,使用普通的autofac API来完成这项工作

我制作了一个示例来重现您的问题:


如果我总结一下,您的问题是Autofac
一次只解析线程

让我们使用另一个代码示例来重现该问题:

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);
    }
}
如果将单个注册的生存期范围从
SingleInstance
更改为
InstancePerDependency
(默认范围),则输出将为:

begin Foo1
begin Foo2
end Foo1
end Foo2
我们可以看到,Autofac在激活共享注册时锁定了共享注册的
IContainer
lock
语句为。 我认为这种行为是为了防止复杂依赖关系图出现问题。ie:如果
Foo1
Foo2
有嵌套依赖关系,会发生什么情况

您将无法绕过
Autofac
的此行为

要更改此行为,您需要更改代码的工作方式。构造函数并不打算花费时间。我建议您将构造函数更改为只执行必需的操作,如果初始化过程需要一些时间,我会推迟它或重构代码,以确保构造函数只需几毫秒即可完成

我创建了一个包装器类来抽象掉所有的autofac DLL


您的核心代码不应该依赖依赖依赖项注入组件。在您的例子中,看起来您使用了
IFrameworkDependencyResolver
接口来延迟加载组件或拥有工厂组件。您应该改用
Func
Lazy
。有关更多信息,请参阅。

您是否尝试使用简单的控制台应用程序重现问题?我尝试过,我的问题也集中在最简单的示例上。我内心深处的解决方案
Class2 ctor ended
Container initialize ended
Start method finished
// For example lets say a protocol takes in ctor these 3 services + runtime configuration object:
public Protocol1(IFramingAgent, IFramingAlgorithm, IFramingParser, configObject configuration)
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
    }
}
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
begin Foo1
begin Foo2
end Foo1
end Foo2