c# - 单例和竞争条件

标签 c# multithreading ninject

与此问题相关:

Null reference in web api call

但是我将其单独生成,因为我认为这可能真的是 Ninject 的一个问题,我不想混淆原始问题,以防我错了。

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

我使用 Nuget 包和一些网络资源设置了 Ninject(抱歉,我似乎没有确切的链接),我认为我做得对。我的项目中有对 Ninject 的引用和 Ninject.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;
    }

我尝试了几种不同的方法来绑定(bind)它(第二种方法是在对这个问题的回答中提出的:Ninject InSingletonScope with Web Api RC),但是当我的 API Controller 在 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;

即使在调试器中 cache , idparam似乎都有值(value)。

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

那么如何在可能会收到来自不同线程的多个请求的 Web api 项目的上下文中安全地创建和注册我的类的单个实例。

最佳答案

如果您使用的是 Ninject.Web.WebApi.WebHost然后 InSingletonScope 将以线程安全的方式为您提供单个实例。

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

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

关于c# - 单例和竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23411316/

相关文章:

c# - 线程池中大量线程处于等待状态,导致性能问题

c++ - 在调用 main() 之前处理需要互斥锁的 C++ 初始化和多线程感知函数?

java - 如何同步一组线程?

c# - 当我创建集合时,Ninject inject 向集合中添加一个元素

c# - 如何在 C# 中实现 ORM

c# - .Net 中的字符串比较 : "+" vs "-"

c# - Ajax 工具包不适用于网站部署

c++ - 如何在旧版 C++ 上创建自己的线程池?

c# - Ninject -> 扫描程序集以匹配接口(interface)并加载为模块

c# - Ninject + ASP.NET Web 表单不工作