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中到达其​​并发集合 , 并行扩展和并行调试选项有望至少*这个头痛的事情会自行消失。

  • 相关阅读:
    hdu 3790 最短路径问题
    hdu 2112 HDU Today
    最短路问题 以hdu1874为例
    hdu 1690 Bus System Floyd
    hdu 2066 一个人的旅行
    hdu 2680 Choose the best route
    hdu 1596 find the safest road
    hdu 1869 六度分离
    hdu 3339 In Action
    序列化和反序列化
  • 原文地址:https://www.cnblogs.com/david1989/p/3693219.html
Copyright © 2011-2022 走看看