Autofac多线程问题 [英] Autofac multithreading issues

查看:133
本文介绍了Autofac多线程问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的应用程序中使用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 . 我认为此行为是为了防止出现复杂的依赖关系图问题.即:如果Foo1Foo2具有嵌套依赖关系,会发生什么?

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆