【问题标题】:Ninject, Parallel.ForEach and InThreadScope()Ninject、Parallel.ForEach 和 InThreadScope()
【发布时间】:2012-03-27 03:04:12
【问题描述】:

对于如何使用 Ninject 实现以下功能,我将不胜感激:

我有一个多线程应用程序。它同时运行大约 20 个非常独立的线程。在应用程序启动时,我使用 InThreadScope() 通过 Ninject 将接口绑定到对象,一切正常。每个线程接收特定于其线程的对象(每个对象的构造函数都会实例化许多线程特定的标志)。

每个线程所做的大部分工作都是等待数据存储完成过程。因此,为了进一步优化线程,我们在主要的 20 个线程中实现了 Parallel.ForEach 逻辑。我希望由 Paralell.ForEach 生成的线程获得与其父线程相同的绑定。但是,我不能简单地将接口重新绑定到 Parallel.ForEach 内的适当对象,我根本不知道绑定的对象是什么 - 在 Paralell.ForEach 内我只能使用接口。

在运行时从内核中检索绑定的正确方法是什么,在 Paralell.ForEach 启动并在循环内重新绑定它们之前?

编辑:尝试包含详细的逻辑/伪代码:

每个线程一旦启动就会执行以下操作:

Kernel.Bind<ILoggingContext>().To<Application1LoggingContext>().InThreadScope();

但是,当 Parallel.ForEach() 从各个线程内部启动时,我不再有权访问 Application1LoggingContext 对象并且无法将 ILoggingContext 重新绑定到它。这是因为 Parallel.ForEach() 是从基类运行的,并且不知道它需要绑定到哪个 Application LoggingContext。这是在每个应用程序中完成的,在启动 20 个大线程时完成。

我想修改基类,即启动 Parallel.ForEach() 并确保在 Parallel.ForEach 线程新创建的每个内部, ILoggingContext 仍然绑定到 Application1LoggingContext - 通常,这样我就可以执行以下操作:

var ctx = Kernel.Get<ILoggingContext>();

【问题讨论】:

  • 能否请您发布您希望如何使用 Paralell.ForEach 的示例代码(伪代码)?

标签: c# multithreading inversion-of-control ninject


【解决方案1】:

看来我已经想通了。这是在 Parallel.Foreach 中重新绑定绑定的通用方法。根据绑定的复杂性/数量,这可能对您有效,也可能不适合您

var logBinding = Kernel.GetBindings(typeof(ILoggingContext)).FirstOrDefault();

Parallel.ForEach(items, n =>
                            {
                                if (Kernel.GetBindings(typeof(ILoggingContext)).Count() == 0 && logBinding != null)
                                    Kernel.AddBinding(logBinding);

                                //do stuff
                                }
                            });

【讨论】:

    【解决方案2】:

    您能否在闭包中捕获 ILoggingContext 以传递给 Parallel.ForEach() 迭代?

    可以从父线程上的上下文感知派生类型 (StartUpApplication1) 调用启动 Parallel.ForEach() 的基类方法 (StartUpBase.Start())。派生类型可以从 Ninject 内核中获取您想要传递给子级的 ILoggingContext,并将其传递给基类方法,然后通过闭包将其传递到并行迭代中:例如:

    abstract class StartUpBase
    {
        public abstract void Start();
    
        protected void StartApplication<T>(ILoggingContext ctx, IEnumerable<T> enumerableWork)
        {
            Parallel.ForEach(enumerableWork, iter =>
                {
                    // this code can refer to ctx e.g.
                    ctx.Log(message);
                });
        }
    }
    
    internal class StartupApplication1<T> : StartUpBase
    {
        // setup of this is elsewhere...
        private IEnumerable<T> _enumerableWork;
    
        public override void Start()
        {
            ILoggingContext ctx = Kernel.Bind<ILoggingContext>()
                .To<Application1LoggingContext>().InThreadScope();
            StartApplication(ctx, _enumerableWork);
        }
    }
    

    【讨论】:

    • 感谢您的回复。不幸的是,Parallel.ForEach 中的代码正在执行 Kernel.Get(),它必须继续这样做
    • 那么从逻辑上讲,如果您坚持使用内核从迭代中获取 ILoggingContext,您将不得不使用 InThreadScope 以外的其他东西,例如自定义提供程序。听起来你让你的生活变得相当复杂!发布更多代码可能会帮助您获得更好的答案。
    • 代码很复杂。我希望能够以某种方式遍历内核中的 Bindings 集合,以通用方式重新绑定 ILoggingContext。
    • 理想情况下,无论如何,只有您的“组合根”(参考:Mark Seemann)才应该了解您的 IoC 容器......在这种情况下,“父线程”似乎履行了组合根 - 也许您可以查看您的子线程是否需要了解 Ninject 内核?
    猜你喜欢
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-06
    相关资源
    最近更新 更多