Autofac-解决多线程环境中的依赖关系 [英] Autofac - resolving dependencies in multi thread environment

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

问题描述

public class MultithreadTester
{

    public void Run()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ManualWork>().As<IWork>();
        builder.RegisterType<ColabManualWork>().As<IColabWork>();
        builder.RegisterType<RelaxAfterManualWork>().As<IRelax>();

        var container = builder.Build();

        //#1 - Simple single thread
        using (var scope = container.BeginLifetimeScope())
        {
            var work = scope.Resolve<IWork>();
            work.DoWork();
        }

        //#2 - Resolving dependecies in worker threads in scopes of these threads without passing lifetime scopes are container into implementation
        using (var scope = container.BeginLifetimeScope())
        {
            var work = scope.Resolve<IColabWork>();
            work.DoWork();
        }

        //#3 - Resolving dependecies in worker threads when original scope is already gone (simulates fast request on same service which spawns threads for request processing)
        IColabWork workForSample3;
        using (var scope = container.BeginLifetimeScope())
        {
            workForSample3 = scope.Resolve<IColabWork>();
        }
        workForSample3.DoWork();

        Console.ReadKey();

    }

    public interface IRelax
    {
        void DoRelax();
    }

    public class RelaxAfterManualWork : IRelax
    {
        public void DoRelax()
        {
            Console.WriteLine("Relaxing after hard work...");
            Thread.Sleep(1000);
            Console.WriteLine("Relax is done...");
        }
    }


    public interface IWork
    {
        void DoWork();
    }

    public class ManualWork : IWork
    {
        private readonly IRelax _relaxActivity;

        public ManualWork(IRelax relaxActivity)
        {
            _relaxActivity = relaxActivity;
        }

        public void DoWork()
        {
            Console.WriteLine("Ufff, this is so hard...");
            Thread.Sleep(5000);
            Console.WriteLine("Work is done...");
            _relaxActivity.DoRelax();
        }
    }

    public interface IColabWork
    {
        void DoWork();
    }

    public class ColabManualWork : IColabWork
    {
        public void DoWork()
        {
            Console.WriteLine("We must discuss how to share the workload...");
            Thread.Sleep(1500);

            Action action = () => 
            {
                //IT WOULD BE FINE TO HAVE RESOLVED DEPENDENCIES PER THREAD AND IN THREAD OWN LIFETIMESCOPE

                Console.WriteLine("Ufff, this is so hard but working with my buddies helps...");
                Thread.Sleep(2500);
                Console.WriteLine("Work is done...");
                var relaxActivity = new RelaxAfterManualWork();
                relaxActivity.DoRelax();
            };

            var thread1 = new Thread(() => { action(); });
            var thread2 = new Thread(() => { action(); });
            thread1.Start();
            thread2.Start();

            thread1.Join();
            thread2.Join();
        }
    }


}

在标记为#1的示例中,我正在解析IWork并执行一些操作.对于单线程环境,我了解DI中的情况,如何使用DI,lifetimescope以及如何解决依赖关系.

In sample marked as #1 I am resolving IWork and run some action. For single thread environment I understand what is going on in DI, how I should work with DI, lifetimescope and how to resolve dependencies.

但是我很难理解多线程环境中的DI.我尝试证明我遇到的一些问题是样本2,样本3.在这些示例中,我将需要解决LifetimeScope中的依赖关系,该依赖关系将为ColabManualWork中的每个线程创建.当然,我不想在Autofac的任何类上进行引用以防止耦合.

But I have trouble to understand DI in multi thread environment. I try to demonstrate some issues I have is samples #2, #3. In these samples I would somehow need to solve dependencies in LifetimeScope which would be created for each threads in ColabManualWork. Of course I do not want references on any class from Autofac to prevent coupling.

我什至创建了一个简单的工厂,该工厂适合于从当前工厂创建嵌套的LifetimeScopes:

I even created simple factory which would be suitable for creating nested LifetimeScopes from current one:

public interface IIsolatedLifetimeScopeFactory<TA>
{
    void Create(Action<TA> action);
}

public class IsolatedLifetimeScopeFactory<TA> : IIsolatedLifetimeScopeFactory<TA>
{
    private readonly ILifetimeScope _scope;

    public IsolatedLifetimeScopeFactory(ILifetimeScope scope)
    {
        _scope = scope;
    }

    public void Create(Action<TA> action)
    {
        using (var subScope = _scope.BeginLifetimeScope())
        {
            var a = subScope.Resolve<TA>();
            action(a);
        }
    }
}

但是我不太喜欢这种解决方案.有三个大问题-1)所有逻辑都必须在lambda函数(或等效方法)中; 2)如果将来再次处置父范围,Autoflac可以重新实现处置子范围的功能(此功能已经使用了几个月); 3)如示例3所示,我可以在启动ColabManualWork中的任何功能之前就处置父级LifetimeScope,因此我的工厂将使用已经处置的LifetimeScope.

But I do not like this solution well. There are three big issues - 1) All logic must be in lambda function (or equivalent method); 2) in future Autoflac can re-implement functionality of disposing child scopes if parent scope is disposed again (this functionality was already here for few months); 3) As demonstrated in sample #3 I can dispose parent LifetimeScope before any functionality in ColabManualWork is even started and thus my factory would be using already disposed LifetimeScope.

有人可以帮助我如何有效地解决工作线程中的问题吗?我读了一些与SimpleInjector相关的东西,名为在多线程中使用依赖项注入应用程序,但我不完全了解它,而且它与Autofac不相关.在那篇文章中写道 在多线程应用程序中,每个线程应获得自己的对象图.这意味着您通常应该在线程执行开始时调用一次container.GetInstance()以获得用于处理该线程的根对象

Can somebody help me how to effectively solve resolving issues in worker threads? I read something related to SimpleInjector named Work with dependency injection in multi-threaded applications but I do not fully get it plus it is not Autofac related. In that article is written In a multi-threaded application, each thread should get its own object graph. This means that you should typically call container.GetInstance() once at the beginning of the thread’s execution to get the root object for processing that thread

如何在不与Autofac耦合的情况下以及与线程相关的Lifetimescope中解决工作线程中的依赖关系?

How to solve dependencies in worker threads without coupling with Autofac and in thread-related lifetimescope?

推荐答案

要为每个线程提供自己的生存期范围,只需将IsolatedLifetimeScopeFactory注册为SingleInstance.这将解决您的担忧2)和3)

To give each thread its own lifetime scope, you just need to register your IsolatedLifetimeScopeFactory as SingleInstance. This will solve your concerns 2) and 3)

[TestMethod]
public void MyTestMethod()
{
    var cb = new ContainerBuilder();
    cb.RegisterGeneric(typeof(IsolatedLifetimeScopeFactory<>))
        .SingleInstance();
    var container = cb.Build();

    using (var scope1 = container.BeginLifetimeScope("scope1"))
    using (var scope2 = scope1.BeginLifetimeScope("scope2"))
    {
        var factory = scope2.Resolve<IsolatedLifetimeScopeFactory<object>>();
        var tag = factory._scope.Tag; // made _scope public for testing purposes
        Assert.AreNotEqual("scope1", tag);
        Assert.AreNotEqual("scope2", tag);

        // This particular string "root" is probably not guaranteed behavior, but
        // being in the root scope is guaranteed for SingleInstance registrations.
        Assert.AreEqual("root", tag);
    }
}

您的关注点1)可以通过使用不同的抽象来解决.例如,您可以将其添加到IsolatedLifetimeScopeFactory

Your concern 1) could be solved by using a different abstraction. For example, you could add this to the IsolatedLifetimeScopeFactory

public Autofac.Features.OwnedInstances.Owned<TA> Create()
{
    return _scope.Resolve<Autofac.Features.OwnedInstances.Owned<TA>>();
}

如果您确实愿意,可以将Owned隐藏在抽象的后面,尽管我会说这太过分了.

And you could hide Owned behind an abstraction if you really wanted to, although I would say that's overkill.

这篇关于Autofac-解决多线程环境中的依赖关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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