xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

ConcurrentDictionary.GetOrAdd のファクトリーメソッドは排他制御されていない

というタイトルの通りなのですが、案外忘れがちです。例えば以下のような期待をしてはいけません。

//--- こんな排他制御機能付きの辞書があるとする
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) で引数で与えたファクトリーメソッドを実行しているのですが、何も排他制御されていません。排他制御がかかっていると思って気を緩めてはいけないので気を付けましょう!