というタイトルの通りなのですが、案外忘れがちです。例えば以下のような期待をしてはいけません。
//--- こんな排他制御機能付きの辞書があるとする var dic= new ConcurrentDictionary<string, int>(); //--- 値がなければ追加したいけれど... var value = dic.GetOrAdd(key, x => { //--- このスコープの処理は排他制御されていないんだZE!! return newValue; });
内部実装を確認
この挙動は Reference Source で .NET Framework の内部実装を見てみれば分かります。ConcurrentDictionary
の実装から GetOrAdd
の部分をピックアップしたのが以下です。
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) { if (key == null) throw new ArgumentNullException("key"); if (valueFactory == null) throw new ArgumentNullException("valueFactory"); TValue resultingValue; if (TryGetValue(key, out resultingValue)) { return resultingValue; } //--- valueFactory の実行が何もロックされていない TryAddInternal(key, valueFactory(key), false, true, out resultingValue); return resultingValue; }
上記の通り valueFactory(key)
で引数で与えたファクトリーメソッドを実行しているのですが、何も排他制御されていません。排他制御がかかっていると思って気を緩めてはいけないので気を付けましょう!