Ninject、Parallel.ForEach和InThreadScope()

5

我希望您可以告诉我如何使用Ninject来实现以下内容:

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

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

在循环开始之前正确地检索内核中的绑定,并以通用方式重新绑定它们的正确方法是什么?

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

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

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

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

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

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

2
请问您能否提供一下使用Parallel.ForEach的示例代码(伪代码)? - Andriy Buday
2个回答

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
                                }
                            });

0

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

您的基类方法(StartUpBase.Start())启动了Parallel.ForEach(),可以从上下文感知的派生类型(StartUpApplication1)在父线程上调用。派生类型可以从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>()操作,并且必须继续执行该操作。 - Igorek
1
那么从逻辑上讲,如果您坚持要在迭代中使用内核来获取ILoggingContext,则必须使用除InThreadScope之外的其他内容,例如自定义提供程序。听起来您正在让自己的生活变得非常复杂!发布更多代码可能会帮助您获得更好的答案。 - James World
代码相当复杂。我希望能以某种方式迭代内核中的绑定集合,以便以通用方式重新绑定ILoggingContext。 - Igorek
理想情况下,只有你的“组合根”(参考:Mark Seemann)应该对你的IoC容器有任何了解...在这种情况下,“父线程”似乎扮演了组合根的角色 - 也许你可以审查一下你的子线程是否需要知道Ninject内核? - James World

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接