zoukankan      html  css  js  c++  java
  • “线程安全的” Dictionary(TKey,TValue)

    这是一篇翻译,专门介绍Dictionary线程安全问题,原文网址如下

    http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/

    翻译的不对之处,请指正。

    介绍

    一个宠物项目,我目前正在研究中需要使用内部字典来存储“注册”的数据,这是一个相当普遍的要求。 对于这个特殊的项目,.net 3.5中,我想至少尝试使其“线程安全”的,

    着眼于将其移动到ConcurrentDictionary,.NET4中保证线程不仅是安全的,而且具有更精细的锁提高多线程性能。

    一个再简单不过的例子,却有许多人犯着这样的错误。

    1.只是锁定写入?

    很明显,我们需要围绕着写同步原语syncronisation primitive类型的操作,但第一印象可能会使你认为读应该没问题-----尤其是如果我们坚持优先TryGetValue模式,而不是“如果它存在,那么得到的值”:

                object myValue;  // This is obviously not thread safe. 
                // Something else can alter the collection 
                // between ContainsKey and reading the 
                // value. 
                if (dictionary.ContainsKey("Testing"))
                {
                    myValue = dictionary["Testing"];
                }
                //Using TryGetValue looks safe though? 
                //Doesn't it?! 
                if (!dictionary.TryGetValue("Testing", out myValue)) throw new KeyNotFoundException();

    不幸的是,如果使用Reflector查看器,看看TryGetValue是如何实现的,作为上面的第一种方法,很明显它具有完全相同的并发问题:

     public bool TryGetValue(TKey key, out TValue value)
            {
                int index = this.FindEntry(key);
                if (index >= 0)
                {
                    value = this.entries[index].value;
                    return true;
                }
                value = default(TValue);
                return false;
            }

    2.因此,我将锁定读取和写入?

    下一个显而易见的方法是要找到我们的代码中无处不在的Dictionary,用于读取或写入,并使用同步原语syncronisation primitive,例如锁 ,以确保我们任何时候只在单个线程访问它:

            private readonly object padlock = new object();
            private readonly Dictionary<string, object> dictionary = new Dictionary<string, object>();
    
            private void Test()
            {
                object myValue;
    
                // Now we lock before we do anything 
    
                lock (padlock)
                {
    
                    if (dictionary.ContainsKey("Testing"))
                    {
                        myValue = dictionary["Testing"];
                    }
                }
                lock (padlock)
                {
    
                    if (!dictionary.TryGetValue("Testing", out myValue)) { }
    
                }
            }

    很简单,但你依赖锁定周围的每一个访问,这不仅难看,而且如果你错过了一个,也可能容易出现错误。  因此,一旦把我们代码迁移到.NET 4。在新的ConcurrentDictionary,我们将不得不通过代码并依次取出每个锁 – 这工作相当辛苦!

    3.组合(组合设计模式的实现)

    在这种方法中,总结了我们在自己的类中使用讨厌地非线程安全的Dictionary,让我使用想用的方法,并采取相应的任何锁。 这个类只实现了“array” 的访问和TryGetValue,但是这个方法足够用了:

      public class SafeDictionary<TKey, TValue>
        {
    
            private readonly object _Padlock = new object();
            private readonly Dictionary<TKey, TValue> _Dictionary = new Dictionary<TKey, TValue>();
            public TValue this[TKey key]
            {
                get
                {
                    lock (_Padlock)
                    {
                        return _Dictionary[key];
                    }
                }
                set
                {
                    lock (_Padlock)
                    {
                        _Dictionary[key] = value;
                    }
                }
            }
            public bool TryGetValue(TKey key, out TValue value)
            {
                lock (_Padlock)
                {
                    return _Dictionary.TryGetValue(key, out value);
                }
            }
        }

    当我们防止任何直接进入Dictionar和每当我们需要访问它并在内部使用我们的锁的时候,我们现在可以使用代码SafeDictionary无需担心并发问题 - 无论是读和写操作!

    4.到 .Net 4 ?

    正如我前面提到的,.NET 4将支持数个“线程安全”的集合,其中包括Dictionary,在新的System.Collections.Concurrent命名空间 。 因此我们有自己实现的SafeDictionary,有以下几个特点:

    ž   我们可以通过我们的代码并替换所有引用SafeDictionary与ConcurrentDictionary。在我们的主代码中没有任何锁,以至于我们可以这个直接替换。

    ž   我们可以改变我们的SafeDictionary内部使用一个ConcurrentDictionary,并删除所有的内部锁。

    ž   如果我们不介意使用额外的方法,我们可以删除所有来自SafeDictionary实例和继承ConcurrentDictionary:

    public class SafeDictionary<Tkey, TValue> : ConcurrentDictionary<Tkey, TValue> 
    { 
     
    } 

    5.结论

    相当长的博客文章提交一个相当简单的问题,但有时候碰巧碰到简单的并发可以是失误和头痛的根源。 一旦.NET 4中到达其​​并发集合 , 并行扩展和并行调试选项有望至少*这个头痛的事情会自行消失。

  • 相关阅读:
    交通综合改造工程EPC总承包项目
    二三维一体化地理信息平台
    NetCore3.1升级到Net5.0序列化方法过时问题
    windows server2012部署.net core IIS,页面报503,应用程序池自动停止。。。
    NetCore使用NPOI导入Word中的图片信息
    NetCore 使用 iTextSharp 读取 PDF 中的文字信息
    NetCore 在 Docker中文件路径找不到的问题
    Vue中数组list直接push的是对象而不是追加数据的问题
    netcore3.1增加阿里云OSS云存储服务
    Centos中Docker容器中程序访问宿主机Redis和Mysql
  • 原文地址:https://www.cnblogs.com/david1989/p/3693219.html
Copyright © 2011-2022 走看看