【问题标题】:Autofac - resolving dependencies in multi thread environmentAutofac - 解决多线程环境中的依赖关系
【发布时间】:2015-07-29 16:03:23
【问题描述】:
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、生命周期以及如何解决依赖关系。

但我在多线程环境中很难理解 DI。我试图证明我遇到的一些问题是示例 #2、#3。在这些示例中,我需要以某种方式解决 LifetimeScope 中的依赖关系,这些依赖关系将为 ColabManualWork 中的每个线程创建。当然,我不想引用 Autofac 中的任何类来防止耦合。

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

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。

有人可以帮助我如何有效地解决工作线程中的问题吗?我读了一些与 SimpleInjector 相关的东西,名为 Work with dependency injection in multi-threaded applications,但我没有完全理解,而且它与 Autofac 无关。那篇文章里写着 在多线程应用程序中,每个线程都应该有自己的对象图。这意味着您通常应该在线程执行开始时调用一次 container.GetInstance() 以获取用于处理该线程的根对象

如何在不耦合 Autofac 和线程相关生命周期的情况下解决工作线程中的依赖关系?

【问题讨论】:

    标签: c# multithreading dependency-injection autofac


    【解决方案1】:

    要为每个线程提供自己的生命周期范围,您只需将您的IsolatedLifetimeScopeFactory 注册为SingleInstance。这将解决您的顾虑 2) 和 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

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

    如果你真的想的话,你可以将Owned 隐藏在一个抽象后面,尽管我会说这太过分了。

    【讨论】:

    • 看来 SingleInstance 可以工作,因为它为我提供了不应该被处置的“根”范围。因此,我可以根据需要调用 BeginLifetimeScope。您对 Owned 的建议似乎也有效。可以使用stackoverflow.com/questions/5438274/… post 与 Autoflac 代码解耦。但是,我宁愿留在我的工厂,与 Owned 相比,这让我可以轻松解决多个依赖项。我什至会说,在我的工厂中使用带有“自己”方法的块或使用兰巴似乎非常相似
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    相关资源
    最近更新 更多