Autofac - 解决多线程环境中的依赖关系

时间:2015-07-29 16:03:24

标签: c# multithreading dependency-injection 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以及如何解决依赖关系。

但我在多线程环境中无法理解DI。我试图证明我的一些问题是样品#2,#3。在这些示例中,我会以某种方式解决LifetimeScope中的依赖关系,这些依赖关系将在ColabManualWork中为每个线程创建。当然我不希望从Autofac的任何类引用来防止耦合。

我甚至创建了一个简单的工厂,它适合从当前的那个创建嵌套的LifetimeScope:

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。

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

如何解决工作线程中的依赖关系而不与Autofac和线程相关的lifetimescope耦合?

1 个答案:

答案 0 :(得分:3)

要为每个帖子提供自己的生命周期范围,您只需将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隐藏在抽象背后,虽然我会说这有点过分。