单身人士和竞争条件

时间:2014-05-01 15:51:44

标签: c# multithreading ninject

与此问题相关:

Null reference in web api call

但是我正在单独产生它,因为我觉得它可能真的是Ninject的一个问题,我不想混淆原来的问题,以防我错了。

我的问题是我有一个使用Ninject来管理依赖注入的web api项目,并且大部分时间它工作正常。问题是如果你在启动应用程序后立即快速启动多个请求,有时它们会以NullReferenceExceptions的某种非常奇怪的方式失败,对于那些看起来不是空的对象我不应该存在。我想知道Ninject使用InSingletonScope是否存在问题并且没有真正给我一个单例,但有时给我一些尚未完成初始化的东西(但可能是在我在调试器中查看它时初始化)。那可能吗?还是我完全咆哮了错误的树?

我使用Nuget软件包和一些网络资源设置Ninject(抱歉,我似乎没有确切链接到哪些)并且我想到我做得对。我的项目中引用了NinjectNinject.Web.Common。在NinjectWebCommon.cs我有这个用于注册一堆服务(我已经排除了大部分服务,仅仅隔离了给我带来麻烦的服务):

    private static void RegisterServices(IKernel kernel)
    {
        System.Diagnostics.Debug.WriteLine("Registering Services...");
        kernel.Bind<Services.IUserService>().To<Services.UserService>().InSingletonScope();                                 

        kernel.Bind<Services.IMyFinderService>().To<Services.MyFinderService>().InSingletonScope();
        //kernel.SingletonBind<Services.IMyFinderService>().To<Services.MyFinderService>();
        //kernel.Bind<Services.IMyFinderService>().ToConstant<Services.MyFinderService>(new Services.MyFinderService(kernel.Get<Services.IUserService>()));
    }  

MyFinderService是给我带来麻烦的那个,请注意它的构造函数是在它之前注册的IUserService(也可能是问题的原因):

    public MyFinderService(IUserService service)
    {
        cache = new Dictionary<Guid, MyClassBase>();
        userService = service;
    }

我尝试了几种不同的方法来绑定它(第二个问题是在这个问题的答案中提出的:Ninject InSingletonScope with Web Api RC)但是当我的API控制器调用这个方法时,它似乎仍然不对在MyFinderService

public MyClassBase GetThing(Guid id)
{
    if (cache.ContainsKey(id))
    {
        return cache[id];
    }
    else
    {
        var type = typeof(MyClassBase).Assembly.GetTypes().FirstOrDefault
        (
            t => t.IsClass &&
                t.Namespace == typeof(MyClassBase).Namespace + ".Foo" &&
                t.IsSubclassOf(typeof(MyClassBase)) &&
                (t.GetCustomAttribute<MyIdAttribute>()).GUID == id
        );
        if (type != null)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("Cache null: {0}",cache == null));
            var param = (MyClassBase)Activator.CreateInstance(type, userService);
            cache[id] = param;
            return param;
        }
        return null;
    }
}

我有时会在这一行上获得NullReferenceException

cache[id] = param;

即使在调试器中cacheidparam似乎都有值。

我的假设是,当我注册某个单独的东西时,Ninject将处理所有细节,确保它确实是一个单例并在延迟初始化期间锁定访问以确保它不会创建一个单独的,但也许我错了吗?

那么如何在web api项目的上下文中安全地创建和注册我的类的单个实例,该项目可能会从不同的线程获得多个请求。

1 个答案:

答案 0 :(得分:3)

如果您正在使用Ninject.Web.WebApi.WebHost,那么InSingletonScope将以线程安全的方式为您提供单个实例。

但不幸的是,有很多博客文章和stackoverflow答案显示使用ActivationBlock错误实现的DependencyResolver实现,即使它们被注册为单例,它也会在请求范围内为您提供对象。

Ninject不做的是确保返回的对象本身是线程安全的。在您的情况下似乎就是这种情况。 Dictionary的内部结构不是线程安全的。只允许一个线程同时修改字典。您应该切换到ConcurrentDictionary