「スレッド安全」Dictionary(TKey,TValue)

9856 ワード

これは翻訳です.Dictorryスレッドの安全問題を紹介します.原文のURLは以下の通りです.
http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/
翻訳の誤りを指摘してください.
紹介する
 
ペットプロジェクトは、内部辞書を使って「登録」のデータを保存することが研究中です.これはかなり一般的な要求です.この特殊なプロジェクトについては.net 3.5の中で、少なくともスレッドを安全にしてみたいです.
NET 4では、これをConcerentDictionaryに移動させることに着目して、スレッドは安全であるだけでなく、より微細なロックでマルチスレッド性能を向上させることが保証されています.
 
もう一つの簡単な例ですが、このような間違いを犯している人がたくさんいます.
 
1.書き込みをロックするだけですか?
明らかに、私たちはげんごを同期させる(syncronisation prmitive)タイプの操作をめぐって書く必要がありますが、第一印象は読むのが大丈夫だと思わせるかもしれません.
 
            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 prmitive)を使用して、例えばロックは、私たちがいつでも単一のスレッドにのみアクセスすることを保証するために使用される.
        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に移行します.新しいConcerentDictionaryでは、コードを通して順番にロックを取り出しなければなりません.
 
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);

            }

        }

    }
私達が直接Dictiorに入ることを防止します.私達はそれを訪問して内部でロックを使う必要があります.コードSafeDictionaryを使って、同時進行の問題を心配する必要がありません.読んでも書いても大丈夫です.
 
4.Net 4
 
NET 4は、前述のように、Dictoraryを含む数の「スレッドセキュリティ」の集合をサポートします.新しいSystem.Collects.Concurrentの名前空間です.したがって、私たちは自分で実現したSafeDictionaryを持っています.以下のいくつかの特徴があります.
ž   私たちはコードを通してSafeDictionaryとConcerentDictionaryを全部交換できます.私たちのメインコードには鍵がありません.これを直接交換できます.
ž   私たちはSafeDictionary内部を変えて、ConcerentDictionaryを使って、内部錠を全部削除します.
ž   追加の方法を使用することを気にしないならば、SafeDictioryのインスタンスとConcerentDictioryをすべて削除することができます.
public class SafeDictionary<Tkey, TValue> : ConcurrentDictionary<Tkey, TValue> 

{ 

 

} 
 
5.結論
 
かなり長いブログの文章はかなり簡単な問題を提出していますが、たまに簡単な同時進行があって、ミスと頭痛の元になります.NET 4でその合併集合に到達すれば、並行拡張と並行デバッグオプションは少なくとも*この頭痛の原因が自分で消えてしまう可能性があります.