【问题标题】:Ninject exception in Parallel.ForeachParallel.Foreach 中的 Ninject 异常
【发布时间】:2011-04-28 14:53:55
【问题描述】:

我有一段代码在要处理的项目列表上运行Parallel.Foreach。每次迭代都会创建几个对象,每个对象都会实例化并处理它自己的 Ninject IKernel 实例。当对象完成它的工作时,IKernel 就会被释放。

也就是说,这段代码在我的 Windows 7、I7 笔记本电脑上运行良好。但是,当我将它推送到运行 Windows 2008 的 VPS 时,我得到了这个异常。异常不会在同一次迭代中发生,有时它会经历 10 次迭代并抛出异常,有时它会经历数百次迭代。显然,这似乎是一个线程问题,但它不会发生在我的 VPS 之外的任何地方。如果重要的话,这将托管在 ASP.NET IIS 中。

System.AggregateException: One or more errors occurred. --->
System.ArgumentOutOfRangeException: Index was out of range. 
    Must be non-negative and less than the size of the collection.  
    Parameter name: index
        at System.Collections.Generic.List`1.RemoveAt(Int32 index)
        at Ninject.KernelBase.Dispose(Boolean disposing)

这是一个sn-p的代码:

//Code that creates and disposes the Ninject kernel
using(ninjectInstance = new NinjectInstance())
{
    using (var unitOfWork = ninjectInstance.Kernel.Get<NinjectUnitOfWork>())
    {
        Init();
        continueValidation = Validate(tran, ofr);
    }
}

public class NinjectInstance : IDisposable
{
    public IKernel Kernel { get; private set; }

    public NinjectInstance() 
    {           
        Kernel = new StandardKernel(
           new NinjectSettings() { AllowNullInjection = true }, 
           new NinjectUnitOfWorkConfigModule());          
    }

    public void Dispose()
    {
        if (Kernel != null)
        {
            Kernel.Dispose();
        }
    }
}   

编辑 1 有一件事是肯定的,这是一个线程安全问题,我不应该为每个应用程序创建多个 IKernel 实例。这是一个理解如何配置适当的范围以实现实体框架上下文线程安全同时保留 UoW 类型方法的问题,其中多个业务层类可以在单个线程内的 UoW 范围内共享相同的 EF 上下文。

【问题讨论】:

    标签: c# .net asp.net ninject ninject-2


    【解决方案1】:

    http://groups.google.com/group/ninject/browse_thread/thread/574cd317d609e764

    正如我告诉你的那样,Ninject 的 ctor 不是线程安全的 atm,除非你使用的是 NOWEB!如果多次创建/处理内核,您将不得不自己同步访问!我仍然建议重新设计您的 UoW 实施!

    【讨论】:

    • 感谢 Remo,在 google 用户组中与您合作。一旦我明白它是什么,我会在这里更新正确的答案。
    【解决方案2】:

    似乎ninjectInstance 是一个实例变量。因此,在并行环境中,ninjectInstance.Dispose() 可能会被调用两次(调用 Kernel.Dispose() 不会将 Kernel 属性设置为 null),因为 Kernel.Dispose() 已经被调用,方法失败。

    也许你想要类似的东西

    using (var ninjectInstance = new NinjectInstance()) {
    ..
    }
    

    【讨论】:

    • 我不认为这是一种可能性,因为只有一个线程(处理特定 Parallel.Foreach 迭代的线程)创建具有类级别 ninjectInstance 的对象,然后在“使用“ 陈述。一旦下一次迭代发生,该对象已经超出范围,下一次迭代就会创建自己的集合。它是实例变量和类作用域的原因是因为它允许从该对象继承的类在 Init() 和 Validate() 方法中轻松使用 base.ninjectInstance.Get。无需我将其作为参数传递。
    • 顺便说一句,为了安全起见,我在 Kernel.Dispose() 之后设置了 Kernel = null,同样的问题。
    • -1 虽然您只是想提供帮助,但这与根本问题无关,也不会在辩论中添加任何事实,抱歉...
    • @Ruben,你为什么不告诉我们根本问题是什么,为什么我的回答“与此无关”?
    • @Remo Gloor 已经解决了它 - OP 发布的代码很好,并且正确使用了 Dispose 模式,即使可能有一些小的样式问题。问题是ninject 实现中相对令人惊讶的缺乏线程安全性。建议按照您的建议重新调整会产生重大影响是不正确。我没有必要教育任何人,只要指出错误的地方,这样可能不熟悉细节的人就不会将猜测与事实混淆。重新阅读我发现这是对您的问题的过度反应,但是....
    猜你喜欢
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    • 2017-06-23
    • 2014-05-18
    • 1970-01-01
    • 2018-06-23
    相关资源
    最近更新 更多