在.NET中将项目添加到Dictionary <>对象时,如何避免双重检查锁定?

No *_*rns 9 c# dictionary locking

我有一个关于提高程序效率的问题.我有一个Dictionary <string,Thingey>定义为名为Thingeys.这是一个Web应用程序,随着时间的推移将创建多个名为Thingey的.Thingey的创建成本有些高昂(并非过分夸大),但我希望尽可能避免使用它.我为请求获得正确的Thingey的逻辑看起来很像这样:

    private Dictionary<string, Thingey> Thingeys;
    public Thingey GetThingey(Request request)
    {
        string thingeyName = request.ThingeyName;
        if (!this.Thingeys.ContainsKey(thingeyName))
        {
            // create a new thingey on 1st reference
            Thingey newThingey = new Thingey(request);
            lock (this.Thingeys)
            {
                if (!this.Thingeys.ContainsKey(thingeyName))
                {
                    this.Thingeys.Add(thingeyName, newThingey);
                }
                // else - oops someone else beat us to it
                // newThingey will eventually get GCed
            }
        }

        return this. Thingeys[thingeyName];
    }
Run Code Online (Sandbox Code Playgroud)

在这个应用程序中,Thingeys一旦创建就永远活着.我们不知道如何创建它们或者在应用程序启动和请求开始进入之前需要哪些.我在上面的代码中遇到的问题是偶尔出现newThingey的实例因为我们同时获得多个请求它在它被创建之前.我们最终创建了其中两个,但只添加了一个.有没有更好的方法来创建和添加Thingeys,不涉及检查/创建/锁定/检查/添加我们创建的罕见外来瘦,但最终永远不会使用?(而且这段代码有效并且已经运行了一段时间.这只是一直困扰着我的唠叨.)

我试图避免在创建Thingey期间锁定字典.

mfe*_*old 7

这是标准的双重检查锁定问题.它在这里实现的方式是不安全的,并且可能导致各种问题 - 如果字典的内部状态搞得太糟糕,可能会在第一次检查时崩溃.

这是不安全的,因为你是在没有同步的情况下检查它,如果运气不好你可以点击它,而其他一些线程正在更新字典的内部状态

一个简单的解决方案是将第一张支票放在锁下.这样做的一个问题是,这成为全局锁定,在负载较重的Web环境中,它可能成为一个严重的瓶颈.

如果我们谈论的是.NET环境,可以通过搭载ASP.NET同步机制来解决这个问题.

以下是我在NDjango渲染引擎中的做法:我为每个渲染线程保留一个全局字典和一个字典.当一个请求到来时,我首先检查本地字典 - 这个检查不必同步,如果那里有东西,我就把它拿走

如果不是我在全局字典上进行同步,请检查它是否存在,以及是否将其添加到我的线程字典中并释放锁定.如果它不在全局词典中,我首先将它添加到那里,同时仍处于锁定状态.

  • 对.ContainsKey的调用发生在锁之外.如果通过调用.Add在另一个线程位于锁定的中途,则字典可能处于无效的中间状态(例如,如果正在调整散列桶的大小).这可能导致字典中的对象看起来丢失,直接崩溃或其他未定义的行为.如果您在锁内发生每次访问,则锁仅保护您. (5认同)